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