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.common.utils.Utils.*;
020import static org.apache.juneau.internal.CollectionUtils.*;
021
022import java.io.*;
023import java.util.*;
024
025import org.apache.juneau.common.utils.*;
026
027/**
028 * Represents a single entry in a configuration.
029 *
030 * This is a read-only object.
031 */
032public class ConfigMapEntry {
033   final String rawLine;
034   final String key, value, comment;
035   final String modifiers;
036   final List<String> preLines;
037
038   static final ConfigMapEntry NULL = new ConfigMapEntry(null, null, null, null, null);
039
040   ConfigMapEntry(String line, List<String> preLines) {
041      this.rawLine = line;
042      var i = line.indexOf('=');
043      var key2 = line.substring(0, i).trim();
044
045      var m1 = key2.indexOf('<');
046      var m2 = key2.indexOf('>');
047
048      modifiers = nullIfEmpty((m1 > -1 && m2 > m1) ? key2.substring(m1+1, m2) : null);
049
050      this.key = m1 == -1 ? key2 : key2.substring(0, m1);
051
052      line = line.substring(i+1);
053
054      i = line.indexOf('#');
055      if (i != -1) {
056         var l2 = Utils.splita(line, '#', 2);
057         line = l2[0];
058         if (l2.length == 2)
059            this.comment = l2[1].trim();
060         else
061            this.comment = null;
062      } else {
063         this.comment = null;
064      }
065
066      this.value = StringUtils.replaceUnicodeSequences(line.trim());
067
068      this.preLines = preLines == null ? Collections.emptyList() : u(copyOf(preLines));
069   }
070
071   ConfigMapEntry(String key, String value, String modifiers, String comment, List<String> preLines) {
072      this.rawLine = null;
073      this.key = key;
074      this.value = value;
075      this.comment = comment;
076      this.modifiers = modifiers;
077      this.preLines = preLines == null ? Collections.emptyList() : u(copyOf(preLines));
078   }
079
080   /**
081    * Returns the name of this entry.
082    *
083    * @return The name of this entry.
084    */
085   public String getKey() {
086      return key;
087   }
088
089   /**
090    * Returns the raw value of this entry.
091    *
092    * @return The raw value of this entry.
093    */
094   public String getValue() {
095      return value;
096   }
097
098   /**
099    * Returns the same-line comment of this entry.
100    *
101    * @return The same-line comment of this entry.
102    */
103   public String getComment() {
104      return comment;
105   }
106
107   /**
108    * Returns the pre-lines of this entry.
109    *
110    * @return The pre-lines of this entry as an unmodifiable list.
111    */
112   public List<String> getPreLines() {
113      return preLines;
114   }
115
116   /**
117    * Returns the modifiers for this entry.
118    *
119    * @return The modifiers for this entry, or <jk>null</jk> 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 (var pl : preLines)
129         w.append(pl).append('\n');
130      if (rawLine != null) {
131         for (var i = 0; i < rawLine.length(); i++) {
132            var 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('<').append(modifiers).append('>');
143         w.append(" = ");
144
145         var val = value;
146         for (var i = 0; i < val.length(); i++) {
147            var 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.of("\\#");
168}