001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.config.internal;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.StringUtils.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022
023import java.io.*;
024import java.util.*;
025
026import org.apache.juneau.commons.lang.*;
027
028/**
029 * Represents a single entry in a configuration.
030 *
031 * This is a read-only object.
032 */
033public class ConfigMapEntry {
034   static final ConfigMapEntry NULL = new ConfigMapEntry(null, null, null, null, null);
035   private static final AsciiSet REPLACE_CHARS = AsciiSet.of("\\#");
036   final String rawLine;
037   final String key, value, comment;
038
039   final String modifiers;
040
041   final List<String> preLines;
042
043   ConfigMapEntry(String line, List<String> preLines) {
044      this.rawLine = line;
045      var i = line.indexOf('=');
046      var key2 = line.substring(0, i).trim();
047
048      var m1 = key2.indexOf('<');
049      var m2 = key2.indexOf('>');
050
051      modifiers = nullIfEmpty((m1 > -1 && m2 > m1) ? key2.substring(m1 + 1, m2) : null);
052
053      this.key = m1 == -1 ? key2 : key2.substring(0, m1);
054
055      line = line.substring(i + 1);
056
057      i = line.indexOf('#');
058      if (i != -1) {
059         var l2 = splita(line, '#', 2);
060         line = l2[0];
061         if (l2.length == 2)
062            this.comment = l2[1].trim();
063         else
064            this.comment = null;
065      } else {
066         this.comment = null;
067      }
068
069      this.value = replaceUnicodeSequences(line.trim());
070
071      this.preLines = preLines == null ? Collections.emptyList() : u(copyOf(preLines));
072   }
073
074   ConfigMapEntry(String key, String value, String modifiers, String comment, List<String> preLines) {
075      this.rawLine = null;
076      this.key = key;
077      this.value = value;
078      this.comment = comment;
079      this.modifiers = modifiers;
080      this.preLines = preLines == null ? Collections.emptyList() : u(copyOf(preLines));
081   }
082
083   /**
084    * Returns the same-line comment of this entry.
085    *
086    * @return The same-line comment of this entry.
087    */
088   public String getComment() { return comment; }
089
090   /**
091    * Returns the name of this entry.
092    *
093    * @return The name of this entry.
094    */
095   public String getKey() { return key; }
096
097   /**
098    * Returns the modifiers for this entry.
099    *
100    * @return The modifiers for this entry, or <jk>null</jk> if it has no modifiers.
101    */
102   public String getModifiers() { return modifiers; }
103
104   /**
105    * Returns the pre-lines of this entry.
106    *
107    * @return The pre-lines of this entry as an unmodifiable list.
108    */
109   public List<String> getPreLines() { return preLines; }
110
111   /**
112    * Returns the raw value of this entry.
113    *
114    * @return The raw value of this entry.
115    */
116   public String getValue() { return value; }
117
118   Writer writeTo(Writer w) throws IOException {
119      if (value == null)
120         return w;
121      for (var pl : preLines)
122         w.append(pl).append('\n');
123      if (nn(rawLine)) {
124         for (var i = 0; i < rawLine.length(); i++) {
125            var c = rawLine.charAt(i);
126            if (c == '\n')
127               w.append('\n').append('\t');
128            else if (c != '\r')
129               w.append(c);
130         }
131         w.append('\n');
132      } else {
133         w.append(key);
134         if (nn(modifiers))
135            w.append('<').append(modifiers).append('>');
136         w.append(" = ");
137
138         var val = value;
139         for (var i = 0; i < val.length(); i++) {
140            var c = val.charAt(i);
141            if (c == '\n')
142               w.append('\n').append('\t');
143            else if (c != '\r') {
144               if (REPLACE_CHARS.contains(c) || (Character.isISOControl(c) && ! (c == '\n' || c == '\r' || c == '\t'))) {
145                  w.append(unicodeSequence(c));
146               } else {
147                  w.append(c);
148               }
149            }
150         }
151
152         if (ne(comment))
153            w.append(" # ").append(comment);
154
155         w.append('\n');
156      }
157      return w;
158   }
159}