MaskCommentsAndModifiers.java revision 3062:15bdc18525ff
1/* 2 * Copyright (c) 2015, 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 */ 25package jdk.jshell; 26 27import java.util.Set; 28import java.util.stream.Collectors; 29import java.util.stream.Stream; 30 31/** 32 * Within a String, mask code comments and ignored modifiers (within context). 33 * 34 * @author Robert Field 35 */ 36class MaskCommentsAndModifiers { 37 38 private final static Set<String> IGNORED_MODIFERS = 39 Stream.of( "public", "protected", "private", "static", "final" ) 40 .collect( Collectors.toSet() ); 41 42 private final StringBuilder sbCleared = new StringBuilder(); 43 private final StringBuilder sbMask = new StringBuilder(); 44 private final String str; 45 private final int length; 46 private final boolean maskModifiers; 47 private int next = 0; 48 private boolean wasMasked = false; 49 private boolean inside = false; 50 51 @SuppressWarnings("empty-statement") 52 public MaskCommentsAndModifiers(String s, boolean maskModifiers) { 53 this.str = s; 54 this.length = s.length(); 55 this.maskModifiers = maskModifiers; 56 do { } while (next()); 57 } 58 59 public String cleared() { 60 return sbCleared.toString(); 61 } 62 63 public String mask() { 64 return sbMask.toString(); 65 } 66 67 public boolean wasMasked() { 68 return wasMasked; 69 } 70 71 /** 72 * Read the next character 73 */ 74 private int read() { 75 if (next >= length) { 76 return -1; 77 } 78 return str.charAt(next++); 79 } 80 81 private void write(StringBuilder sb, int ch) { 82 sb.append((char)ch); 83 } 84 85 private void write(int ch) { 86 write(sbCleared, ch); 87 write(sbMask, Character.isWhitespace(ch) ? ch : ' '); 88 } 89 90 private void writeMask(int ch) { 91 wasMasked = true; 92 write(sbMask, ch); 93 write(sbCleared, Character.isWhitespace(ch) ? ch : ' '); 94 } 95 96 private void write(CharSequence s) { 97 for (int cp : s.chars().toArray()) { 98 write(cp); 99 } 100 } 101 102 private void writeMask(CharSequence s) { 103 for (int cp : s.chars().toArray()) { 104 writeMask(cp); 105 } 106 } 107 108 private boolean next() { 109 return next(read()); 110 } 111 112 private boolean next(int c) { 113 if (c < 0) { 114 return false; 115 } 116 117 if (c == '\'' || c == '"') { 118 inside = true; 119 write(c); 120 int match = c; 121 c = read(); 122 while (c != match) { 123 if (c < 0) { 124 return false; 125 } 126 if (c == '\n' || c == '\r') { 127 write(c); 128 return true; 129 } 130 if (c == '\\') { 131 write(c); 132 c = read(); 133 } 134 write(c); 135 c = read(); 136 } 137 write(c); 138 return true; 139 } 140 141 if (c == '/') { 142 c = read(); 143 if (c == '*') { 144 writeMask('/'); 145 writeMask(c); 146 int prevc = 0; 147 while ((c = read()) != '/' || prevc != '*') { 148 if (c < 0) { 149 return false; 150 } 151 writeMask(c); 152 prevc = c; 153 } 154 writeMask(c); 155 return true; 156 } else if (c == '/') { 157 writeMask('/'); 158 writeMask(c); 159 while ((c = read()) != '\n' && c != '\r') { 160 if (c < 0) { 161 return false; 162 } 163 writeMask(c); 164 } 165 writeMask(c); 166 return true; 167 } else { 168 inside = true; 169 write('/'); 170 // read character falls through 171 } 172 } 173 174 if (Character.isJavaIdentifierStart(c)) { 175 if (maskModifiers && !inside) { 176 StringBuilder sb = new StringBuilder(); 177 do { 178 write(sb, c); 179 c = read(); 180 } while (Character.isJavaIdentifierPart(c)); 181 String id = sb.toString(); 182 if (IGNORED_MODIFERS.contains(id)) { 183 writeMask(sb); 184 } else { 185 write(sb); 186 if (id.equals("import")) { 187 inside = true; 188 } 189 } 190 return next(c); // recurse to handle left-over character 191 } 192 } else if (!Character.isWhitespace(c)) { 193 inside = true; 194 } 195 196 if (c < 0) { 197 return false; 198 } 199 write(c); 200 return true; 201 } 202} 203