1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package optkl.codebuilders;
 26 
 27 /**
 28  * The base abstract class for a slew of fluent style builders.
 29  * <p>
 30  * At this level the builder just deals with basic appending, indenting, newline handling
 31  *
 32  * <pre>
 33  *     TextBuilder textBuilder = ....;
 34  *     textBuilder
 35  *        .nl()
 36  *        .in()
 37  *        .append("hello)
 38  *        .space()
 39  *        .append("world")
 40  *        .out();
 41  *
 42  * </pre>
 43  *
 44  * @author Gary Frost
 45  */
 46 
 47 
 48 public abstract class TextBuilder<T extends TextBuilder<T>> {
 49     public static class State {
 50         private final StringBuilder stringBuilder = new StringBuilder();
 51         final public boolean indenting = true;
 52         private int indent = 0;
 53         private final String indentation = "    ";
 54         private boolean newLined = true;
 55         public void indentation() {
 56             for (int i = 0; i < indent; i++) {
 57                 stringBuilder.append(indentation);
 58             }
 59         }
 60 
 61         public void indentIfNeededAndAppend(String text) {
 62             if (indenting && newLined) {
 63                 indentation();
 64             }
 65             newLined = false;
 66             stringBuilder.append(text);
 67         }
 68 
 69         public void preformatted(String text){
 70             stringBuilder.append(text);
 71         }
 72 
 73         public void incIndent() {
 74             indent++;
 75         }
 76         public void decIndent() {
 77             indent--;
 78         }
 79 
 80         public void nl() {
 81             newLined = true;
 82         }
 83 
 84         @Override
 85         public String toString(){
 86             return stringBuilder.toString();
 87         }
 88     }
 89 
 90     private State state = new State();
 91 
 92     public T clear() {
 93         state = new State();
 94         return self();
 95     }
 96 
 97     public String getText() {
 98         return toString();
 99     }
100 
101 
102 
103     @SuppressWarnings("unchecked")
104     public T self() {
105         return (T) this;
106     }
107 
108     private static String escape(char ch) {
109         return switch (ch) {
110             case '\b' -> "\\b";
111             case '\f' -> "\\f";
112             case '\n' -> "\\n";
113             case '\r' -> "\\r";
114             case '\t' -> "\\t";
115             case '\'' -> "\\'";
116             case '\"' -> "\\\"";
117             case '\\' -> "\\\\";
118             default -> (ch >= ' ' && ch <= '~') ? String.valueOf(ch) : String.format("\\u%04x", (int) ch);
119         };
120     }
121 
122     public T escaped(String text) {
123         StringBuilder buf = new StringBuilder();
124         for (int i = 0; i < text.length(); i++) {
125             buf.append(escape(text.charAt(i)));
126         }
127         return emitText(buf.toString());
128     }
129 
130      protected T emitText(String text) {
131         state.indentIfNeededAndAppend(text);
132         return self();
133     }
134 
135     public T preformatted(String text){
136         state.preformatted(text);
137         return self();
138     }
139 
140 
141     public T identifier(String text, int  padWidth) {
142         return emitText(text).emitText(" ".repeat(padWidth-text.length()));
143     }
144 
145 
146     public T intValue(int i) {
147         return emitText(Integer.toString(i));
148     }
149     public T intHexValue(int i) {
150         return emitText("0x").emitText(Integer.toHexString(i));
151     }
152 
153     public final T literal(int i) {
154         return emitText(Integer.toString(i));
155     }
156 
157     public final T literal(long i) {
158         return emitText(Long.toString(i));
159     }
160 
161     public T in() {
162         state.incIndent();
163         return self();
164     }
165 
166     public T out() {
167         state.decIndent();
168         return self();
169     }
170     public T nl() {
171         emitText("\n");
172         state.nl();
173         return self();
174     }
175     @Override
176     public final String toString() {
177         return state.toString();
178     }
179 
180 
181 
182 
183 }