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         if (cffv == null)
 63             cffv = ClassFileFormatVersion.CURRENT_PREVIEW_FEATURES; // Aggressive fallback
 64         try {
 65             return AccessFlag.maskToAccessFlags(mask, location, cffv);
 66         } catch (IllegalArgumentException ex) {
 67             mask &= location.flagsMask(cffv);
 68             report("Access Flags: " + ex.getMessage());
 69             return AccessFlag.maskToAccessFlags(mask, location, cffv);
 70         }
 71     }
 72 
 73     protected void print(String s) {
 74         lineWriter.print(s);
 75     }
 76 
 77     protected void print(Object o) {
 78         lineWriter.print(o == null ? null : o.toString());
 79     }
 80 
 81     protected void print(Supplier<Object> safeguardedCode) {
 82         try {
 83             print(safeguardedCode.get());
 84         } catch (IllegalArgumentException e) {
 85             print(report(e));
 86         }
 87     }
 88 
 89     protected void println() {
 90         lineWriter.println();
 91     }
 92 
 93     protected void println(String s) {
 94         lineWriter.print(s);
 95         lineWriter.println();
 96     }
 97 
 98     protected void println(Object o) {
 99         lineWriter.print(o == null ? null : o.toString());
100         lineWriter.println();
101     }
102 
103     protected void println(Supplier<Object> safeguardedCode) {
104         print(safeguardedCode);
105         lineWriter.println();
106     }
107 
108     protected void indent(int delta) {
109         lineWriter.indent(delta);
110     }
111 
112     protected void tab() {
113         lineWriter.tab();
114     }
115 
116     protected void setPendingNewline(boolean b) {
117         lineWriter.pendingNewline = b;
118     }
119 
120     protected String report(Exception e) {
121         out.println("Error: " + e.getMessage()); // i18n?
122         errorReported = true;
123         return "???";
124     }
125 
126     protected String report(String msg) {
127         out.println("Error: " + msg); // i18n?
128         errorReported = true;
129         return "???";
130     }
131 
132     protected String space(int w) {
133         if (w < spaces.length && spaces[w] != null)
134             return spaces[w];
135 
136         StringBuilder sb = new StringBuilder();
137         for (int i = 0; i < w; i++)
138             sb.append(" ");
139 
140         String s = sb.toString();
141         if (w < spaces.length)
142             spaces[w] = s;
143 
144         return s;
145     }
146 
147     private String[] spaces = new String[80];
148 
149     private LineWriter lineWriter;
150     private PrintWriter out;
151     protected Messages messages;
152     protected boolean errorReported;
153 
154     private static class LineWriter {
155         static LineWriter instance(Context context) {
156             LineWriter instance = context.get(LineWriter.class);
157             if (instance == null)
158                 instance = new LineWriter(context);
159             return instance;
160         }
161 
162         protected LineWriter(Context context) {
163             context.put(LineWriter.class, this);
164             Options options = Options.instance(context);
165             indentWidth = options.indentWidth;
166             tabColumn = options.tabColumn;
167             out = context.get(PrintWriter.class);
168             buffer = new StringBuilder();
169         }
170 
171         protected void print(String s) {
172             if (pendingNewline) {
173                 println();
174                 pendingNewline = false;
175             }
176             if (s == null)
177                 s = "null";
178             for (int i = 0; i < s.length(); i++) {
179                 char c = s.charAt(i);
180                 switch (c) {
181                     case ' ':
182                         pendingSpaces++;
183                         break;
184 
185                     case '\n':
186                         println();
187                         break;
188 
189                     default:
190                         if (buffer.length() == 0)
191                             indent();
192                         if (pendingSpaces > 0) {
193                             for (int sp = 0; sp < pendingSpaces; sp++)
194                                 buffer.append(' ');
195                             pendingSpaces = 0;
196                         }
197                         buffer.append(c);
198                 }
199             }
200 
201         }
202 
203         protected void println() {
204             // ignore/discard pending spaces
205             pendingSpaces = 0;
206             out.println(buffer);
207             buffer.setLength(0);
208         }
209 
210         protected void indent(int delta) {
211             indentCount += delta;
212         }
213 
214         protected void tab() {
215             int col = indentCount * indentWidth + tabColumn;
216             pendingSpaces += (col <= buffer.length() ? 1 : col - buffer.length());
217         }
218 
219         private void indent() {
220             pendingSpaces += (indentCount * indentWidth);
221         }
222 
223         private final PrintWriter out;
224         private final StringBuilder buffer;
225         private int indentCount;
226         private final int indentWidth;
227         private final int tabColumn;
228         private boolean pendingNewline;
229         private int pendingSpaces;
230     }
231 }
232