1 /*
  2  * Copyright (c) 2007, 2025, 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 
 26 package com.sun.tools.javap;
 27 
 28 import java.io.PrintWriter;
 29 import java.lang.classfile.AccessFlags;
 30 import java.lang.reflect.AccessFlag;
 31 import java.lang.reflect.ClassFileFormatVersion;
 32 import java.lang.reflect.Modifier;
 33 import java.util.EnumMap;
 34 import java.util.Map;
 35 import java.util.Set;
 36 import java.util.function.Supplier;
 37 
 38 /*
 39  *  A writer similar to a PrintWriter but which does not hide exceptions.
 40  *  The standard print calls are line-buffered; report calls write messages directly.
 41  *
 42  *  <p><b>This is NOT part of any supported API.
 43  *  If you write code that depends on this, you do so at your own risk.
 44  *  This code and its internal interfaces are subject to change or
 45  *  deletion without notice.</b>
 46  */
 47 public class BasicWriter {
 48 
 49     protected BasicWriter(Context context) {
 50         lineWriter = LineWriter.instance(context);
 51         out = context.get(PrintWriter.class);
 52         messages = context.get(Messages.class);
 53         if (messages == null)
 54             throw new AssertionError();
 55     }
 56 
 57     protected Set<AccessFlag> flagsReportUnknown(AccessFlags flags, ClassFileFormatVersion cffv) {
 58         return maskToAccessFlagsReportUnknown(flags.flagsMask(), flags.location(), cffv);
 59     }
 60 
 61     protected Set<AccessFlag> maskToAccessFlagsReportUnknown(int mask, AccessFlag.Location location, ClassFileFormatVersion cffv) {
 62         try {
 63             return AccessFlag.maskToAccessFlags(mask, location, cffv);
 64         } catch (IllegalArgumentException ex) {
 65             mask &= location.flagsMask(cffv);
 66             report("Access Flags: " + ex.getMessage());
 67             return AccessFlag.maskToAccessFlags(mask, location, cffv);
 68         }
 69     }
 70 
 71     protected void print(String s) {
 72         lineWriter.print(s);
 73     }
 74 
 75     protected void print(Object o) {
 76         lineWriter.print(o == null ? null : o.toString());
 77     }
 78 
 79     protected void print(Supplier<Object> safeguardedCode) {
 80         try {
 81             print(safeguardedCode.get());
 82         } catch (IllegalArgumentException e) {
 83             print(report(e));
 84         }
 85     }
 86 
 87     protected void println() {
 88         lineWriter.println();
 89     }
 90 
 91     protected void println(String s) {
 92         lineWriter.print(s);
 93         lineWriter.println();
 94     }
 95 
 96     protected void println(Object o) {
 97         lineWriter.print(o == null ? null : o.toString());
 98         lineWriter.println();
 99     }
100 
101     protected void println(Supplier<Object> safeguardedCode) {
102         print(safeguardedCode);
103         lineWriter.println();
104     }
105 
106     protected void indent(int delta) {
107         lineWriter.indent(delta);
108     }
109 
110     protected void tab() {
111         lineWriter.tab();
112     }
113 
114     protected void setPendingNewline(boolean b) {
115         lineWriter.pendingNewline = b;
116     }
117 
118     protected String report(Exception e) {
119         out.println("Error: " + e.getMessage()); // i18n?
120         errorReported = true;
121         return "???";
122     }
123 
124     protected String report(String msg) {
125         out.println("Error: " + msg); // i18n?
126         errorReported = true;
127         return "???";
128     }
129 
130     protected String space(int w) {
131         if (w < spaces.length && spaces[w] != null)
132             return spaces[w];
133 
134         StringBuilder sb = new StringBuilder();
135         for (int i = 0; i < w; i++)
136             sb.append(" ");
137 
138         String s = sb.toString();
139         if (w < spaces.length)
140             spaces[w] = s;
141 
142         return s;
143     }
144 
145     private String[] spaces = new String[80];
146 
147     private LineWriter lineWriter;
148     private PrintWriter out;
149     protected Messages messages;
150     protected boolean errorReported;
151 
152     private static class LineWriter {
153         static LineWriter instance(Context context) {
154             LineWriter instance = context.get(LineWriter.class);
155             if (instance == null)
156                 instance = new LineWriter(context);
157             return instance;
158         }
159 
160         protected LineWriter(Context context) {
161             context.put(LineWriter.class, this);
162             Options options = Options.instance(context);
163             indentWidth = options.indentWidth;
164             tabColumn = options.tabColumn;
165             out = context.get(PrintWriter.class);
166             buffer = new StringBuilder();
167         }
168 
169         protected void print(String s) {
170             if (pendingNewline) {
171                 println();
172                 pendingNewline = false;
173             }
174             if (s == null)
175                 s = "null";
176             for (int i = 0; i < s.length(); i++) {
177                 char c = s.charAt(i);
178                 switch (c) {
179                     case ' ':
180                         pendingSpaces++;
181                         break;
182 
183                     case '\n':
184                         println();
185                         break;
186 
187                     default:
188                         if (buffer.length() == 0)
189                             indent();
190                         if (pendingSpaces > 0) {
191                             for (int sp = 0; sp < pendingSpaces; sp++)
192                                 buffer.append(' ');
193                             pendingSpaces = 0;
194                         }
195                         buffer.append(c);
196                 }
197             }
198 
199         }
200 
201         protected void println() {
202             // ignore/discard pending spaces
203             pendingSpaces = 0;
204             out.println(buffer);
205             buffer.setLength(0);
206         }
207 
208         protected void indent(int delta) {
209             indentCount += delta;
210         }
211 
212         protected void tab() {
213             int col = indentCount * indentWidth + tabColumn;
214             pendingSpaces += (col <= buffer.length() ? 1 : col - buffer.length());
215         }
216 
217         private void indent() {
218             pendingSpaces += (indentCount * indentWidth);
219         }
220 
221         private final PrintWriter out;
222         private final StringBuilder buffer;
223         private int indentCount;
224         private final int indentWidth;
225         private final int tabColumn;
226         private boolean pendingNewline;
227         private int pendingSpaces;
228     }
229 }
230