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.marshall.ini;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.io.*;
022
023import org.apache.juneau.marshall.*;
024import org.apache.juneau.marshall.serializer.*;
025
026/**
027 * Specialized writer for serializing INI format.
028 *
029 * <p>
030 * Extends {@link SerializerWriter} with INI-specific methods for sections, key-value pairs,
031 * comments, and quoted strings.
032 *
033 * <h5 class='section'>Notes:</h5><ul>
034 *    <li class='note'>This class is not intended for external use.
035 * </ul>
036 *
037 * <h5 class='section'>See Also:</h5><ul>
038 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Ini">INI Basics</a>
039 * </ul>
040 */
041@SuppressWarnings({
042   "resource" // Writer resource managed by calling code
043})
044public class IniWriter extends SerializerWriter<IniWriter> {
045
046   /** Key-value separator character. */
047   protected final char kvSeparator;
048
049   /** Whether to add spaces around the separator. */
050   protected final boolean spacedSeparator;
051
052   /**
053    * Constructor.
054    *
055    * @param out The writer being wrapped.
056    * @param useWhitespace If <jk>true</jk>, use blank lines between sections.
057    * @param maxIndent The maximum indentation level (unused for INI).
058    * @param trimStrings If <jk>true</jk>, trim strings before serialization.
059    * @param kvSeparator The key-value separator ('=' or ':').
060    * @param spacedSeparator If <jk>true</jk>, add spaces around separator.
061    * @param uriResolver The URI resolver.
062    */
063   protected IniWriter(Writer out, boolean useWhitespace, int maxIndent, boolean trimStrings, char kvSeparator, boolean spacedSeparator, UriResolver uriResolver) {
064      super(out, useWhitespace, maxIndent, trimStrings, '\'', uriResolver);
065      this.kvSeparator = kvSeparator;
066      this.spacedSeparator = spacedSeparator;
067   }
068
069   /**
070    * Writes a section header with optional preceding blank line.
071    *
072    * @param name The section name or path (e.g. "address" or "employment/company").
073    *    <br>Must not be <jk>null</jk>.
074    * @return This object.
075    */
076   public IniWriter section(String name) {
077      w('\n');
078      w('[').w(name).w(']').w('\n');
079      return this;
080   }
081
082   /**
083    * Writes a comment line.
084    *
085    * @param text The comment text.
086    *    <br>Can be <jk>null</jk> (writes an empty comment).
087    * @return This object.
088    */
089   public IniWriter comment(String text) {
090      w("# ");
091      if (ine(text)) {
092         var lines = text.split("\n");
093         for (var i = 0; i < lines.length; i++) {
094            if (i > 0)
095               w("\n# ");
096            w(lines[i]);
097         }
098      }
099      w('\n');
100      return this;
101   }
102
103   /**
104    * Writes a key-value pair.
105    *
106    * @param key The key name.
107    *    <br>Must not be <jk>null</jk>.
108    * @param value The value (already formatted as string).
109    *    <br>Can be <jk>null</jk> (written as the literal <js>"null"</js>).
110    * @return This object.
111    */
112   public IniWriter keyValue(String key, String value) {
113      w(key);
114      if (spacedSeparator)
115         w(" ").w(kvSeparator).w(" ");
116      else
117         w(kvSeparator);
118      w(or(value, "null"));
119      w('\n');
120      return this;
121   }
122
123   /**
124    * Writes a single-quoted string value with escaping.
125    *
126    * <p>
127    * Single quotes within the value are escaped as <c>''</c>.
128    *
129    * @param value The raw string value.
130    *    <br>Can be <jk>null</jk> (emitted as the literal <js>"null"</js>).
131    * @return This object.
132    */
133   public IniWriter quotedString(String value) {
134      if (value == null) {
135         w("null");
136         return this;
137      }
138      w('\'').w(value.replace("'", "''")).w('\'');
139      return this;
140   }
141
142   /**
143    * Writes a blank line.
144    *
145    * @return This object.
146    */
147   public IniWriter blankLine() {
148      w('\n');
149      return this;
150   }
151}