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.internal.CollectionUtils.*;
016import static org.apache.juneau.internal.StringUtils.*;
017
018import java.io.*;
019import java.util.*;
020
021import org.apache.juneau.internal.*;
022
023/**
024 * Represents a single entry in a configuration.
025 *
026 * This is a read-only object.
027 */
028public class ConfigEntry {
029   final String rawLine;
030   final String key, value, comment;
031   final String modifiers;
032   final List<String> preLines;
033
034   static final ConfigEntry NULL = new ConfigEntry(null, null, null, null, null);
035
036   private final static AsciiSet MOD_CHARS = AsciiSet.create("#$%&*+^@~");
037
038   ConfigEntry(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 modIndex = key.length();
044      for (int j = key.length()-1; j > 0; j--)
045         if (MOD_CHARS.contains(key.charAt(j)))
046            modIndex--;
047
048      this.modifiers = key.substring(modIndex);
049      this.key = key.substring(0, modIndex);
050
051      line = line.substring(i+1);
052
053      i = line.indexOf('#');
054      if (i != -1) {
055         String[] l2 = StringUtils.split(line, '#', 2);
056         line = l2[0];
057         if (l2.length == 2)
058            this.comment = l2[1].trim();
059         else
060            this.comment = null;
061      } else {
062         this.comment = null;
063      }
064
065      this.value = StringUtils.replaceUnicodeSequences(line.trim());
066
067      this.preLines = immutableList(preLines);
068   }
069
070   ConfigEntry(String key, String value, String modifiers, String comment, List<String> preLines) {
071      this.rawLine = null;
072      this.key = key;
073      this.value = value;
074      this.comment = comment;
075      this.modifiers = modifiers;
076      this.preLines = immutableList(preLines);
077   }
078
079   /**
080    * Returns the raw value of this entry.
081    *
082    * @return The raw value of this entry.
083    */
084   public String getValue() {
085      return value;
086   }
087
088   /**
089    * Returns the same-line comment of this entry.
090    *
091    * @return The same-line comment of this entry.
092    */
093   public String getComment() {
094      return comment;
095   }
096
097   /**
098    * Returns the pre-lines of this entry.
099    *
100    * @return The pre-lines of this entry as an unmodifiable list.
101    */
102   public List<String> getPreLines() {
103      return preLines;
104   }
105
106   /**
107    * Returns whether this entry has the specified modifier.
108    *
109    * @param m The modifier character.
110    * @return <jk>true</jk> if this entry is encoded.
111    */
112   public boolean hasModifier(char m) {
113      return modifiers.indexOf(m) != -1;
114   }
115
116   /**
117    * Returns the modifiers for this entry.
118    *
119    * @return The modifiers for this entry, or an empty string if it has no modifiers.
120    */
121   public String getModifiers() {
122      return modifiers;
123   }
124
125   Writer writeTo(Writer w) throws IOException {
126      if (value == null)
127         return w;
128      for (String pl : preLines)
129         w.append(pl).append('\n');
130      if (rawLine != null) {
131         for (int i = 0; i < rawLine.length(); i++) {
132            char c = rawLine.charAt(i);
133            if (c == '\n')
134               w.append('\n').append('\t');
135            else if (c != '\r')
136               w.append(c);
137         }
138         w.append('\n');
139      } else {
140         w.append(key);
141         if (modifiers != null)
142            w.append(modifiers);
143         w.append(" = ");
144
145         String val = value;
146         for (int i = 0; i < val.length(); i++) {
147            char c = val.charAt(i);
148            if (c == '\n')
149               w.append('\n').append('\t');
150            else if (c != '\r') {
151               if (REPLACE_CHARS.contains(c) || (Character.isISOControl(c) && ! (c == '\n' || c == '\r' || c == '\t'))) {
152                  w.append(StringUtils.unicodeSequence(c));
153               } else {
154                  w.append(c);
155               }
156            }
157         }
158
159         if (isNotEmpty(comment))
160            w.append(" # ").append(comment);
161
162         w.append('\n');
163      }
164      return w;
165   }
166
167   private static final AsciiSet REPLACE_CHARS = AsciiSet.create("\\#");
168}