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