1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/test/tools/javac/diags/MessageFile.java Wed Jan 26 13:45:25 2011 -0800 1.3 @@ -0,0 +1,463 @@ 1.4 +/* 1.5 + * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.23 + * or visit www.oracle.com if you need additional information or have any 1.24 + * questions. 1.25 + */ 1.26 + 1.27 +import java.io.*; 1.28 +import java.util.*; 1.29 +import java.util.regex.Matcher; 1.30 +import java.util.regex.Pattern; 1.31 + 1.32 +/** 1.33 + * Class to facilitate manipulating compiler.properties. 1.34 + */ 1.35 +class MessageFile { 1.36 + static final Pattern emptyOrCommentPattern = Pattern.compile("( *#.*)?"); 1.37 + static final Pattern infoPattern = Pattern.compile("# ([0-9]+: [-A-Za-z ]+, )*[0-9]+: [-A-Za-z ]+"); 1.38 + 1.39 + /** 1.40 + * A line of text within the message file. 1.41 + * The lines form a doubly linked list for simple navigation. 1.42 + */ 1.43 + class Line { 1.44 + String text; 1.45 + Line prev; 1.46 + Line next; 1.47 + 1.48 + Line(String text) { 1.49 + this.text = text; 1.50 + } 1.51 + 1.52 + boolean isEmptyOrComment() { 1.53 + return emptyOrCommentPattern.matcher(text).matches(); 1.54 + } 1.55 + 1.56 + boolean isInfo() { 1.57 + return infoPattern.matcher(text).matches(); 1.58 + } 1.59 + 1.60 + boolean hasContinuation() { 1.61 + return (next != null) && text.endsWith("\\"); 1.62 + } 1.63 + 1.64 + Line insertAfter(String text) { 1.65 + Line l = new Line(text); 1.66 + insertAfter(l); 1.67 + return l; 1.68 + } 1.69 + 1.70 + void insertAfter(Line l) { 1.71 + assert prev == null && next == null; 1.72 + l.prev = this; 1.73 + l.next = next; 1.74 + if (next == null) 1.75 + lastLine = l; 1.76 + else 1.77 + next.prev = l; 1.78 + next = l; 1.79 + } 1.80 + 1.81 + Line insertBefore(String text) { 1.82 + Line l = new Line(text); 1.83 + insertBefore(l); 1.84 + return l; 1.85 + } 1.86 + 1.87 + void insertBefore(Line l) { 1.88 + assert prev == null && next == null; 1.89 + l.prev = prev; 1.90 + l.next = this; 1.91 + if (prev == null) 1.92 + firstLine = l; 1.93 + else 1.94 + prev.next = l; 1.95 + prev = l; 1.96 + } 1.97 + 1.98 + void remove() { 1.99 + if (prev == null) 1.100 + firstLine = next; 1.101 + else 1.102 + prev.next = next; 1.103 + if (next == null) 1.104 + lastLine = prev; 1.105 + else 1.106 + next.prev = prev; 1.107 + prev = null; 1.108 + next = null; 1.109 + } 1.110 + } 1.111 + 1.112 + /** 1.113 + * A message within the message file. 1.114 + * A message is a series of lines containing a "name=value" property, 1.115 + * optionally preceded by a comment describing the use of placeholders 1.116 + * such as {0}, {1}, etc within the property value. 1.117 + */ 1.118 + static final class Message { 1.119 + final Line firstLine; 1.120 + private Info info; 1.121 + 1.122 + Message(Line l) { 1.123 + firstLine = l; 1.124 + } 1.125 + 1.126 + boolean needInfo() { 1.127 + Line l = firstLine; 1.128 + while (true) { 1.129 + if (l.text.matches(".*\\{[0-9]+\\}.*")) 1.130 + return true; 1.131 + if (!l.hasContinuation()) 1.132 + return false; 1.133 + l = l.next; 1.134 + } 1.135 + } 1.136 + 1.137 + Set<Integer> getPlaceholders() { 1.138 + Pattern p = Pattern.compile("\\{([0-9]+)\\}"); 1.139 + Set<Integer> results = new TreeSet<Integer>(); 1.140 + Line l = firstLine; 1.141 + while (true) { 1.142 + Matcher m = p.matcher(l.text); 1.143 + while (m.find()) 1.144 + results.add(Integer.parseInt(m.group(1))); 1.145 + if (!l.hasContinuation()) 1.146 + return results; 1.147 + l = l.next; 1.148 + } 1.149 + } 1.150 + 1.151 + /** 1.152 + * Get the Info object for this message. It may be empty if there 1.153 + * if no comment preceding the property specification. 1.154 + */ 1.155 + Info getInfo() { 1.156 + if (info == null) { 1.157 + Line l = firstLine.prev; 1.158 + if (l != null && l.isInfo()) 1.159 + info = new Info(l.text); 1.160 + else 1.161 + info = new Info(); 1.162 + } 1.163 + return info; 1.164 + } 1.165 + 1.166 + /** 1.167 + * Set the Info for this message. 1.168 + * If there was an info comment preceding the property specification, 1.169 + * it will be updated; otherwise, one will be inserted. 1.170 + */ 1.171 + void setInfo(Info info) { 1.172 + this.info = info; 1.173 + Line l = firstLine.prev; 1.174 + if (l != null && l.isInfo()) 1.175 + l.text = info.toComment(); 1.176 + else 1.177 + firstLine.insertBefore(info.toComment()); 1.178 + } 1.179 + 1.180 + /** 1.181 + * Get all the lines pertaining to this message. 1.182 + */ 1.183 + List<Line> getLines(boolean includeAllPrecedingComments) { 1.184 + List<Line> lines = new ArrayList<Line>(); 1.185 + Line l = firstLine; 1.186 + if (includeAllPrecedingComments) { 1.187 + // scan back to find end of prev message 1.188 + while (l.prev != null && l.prev.isEmptyOrComment()) 1.189 + l = l.prev; 1.190 + // skip leading blank lines 1.191 + while (l.text.isEmpty()) 1.192 + l = l.next; 1.193 + } else { 1.194 + if (l.prev != null && l.prev.isInfo()) 1.195 + l = l.prev; 1.196 + } 1.197 + 1.198 + // include any preceding lines 1.199 + for ( ; l != firstLine; l = l.next) 1.200 + lines.add(l); 1.201 + 1.202 + // include message lines 1.203 + for (l = firstLine; l != null && l.hasContinuation(); l = l.next) 1.204 + lines.add(l); 1.205 + lines.add(l); 1.206 + 1.207 + // include trailing blank line if present 1.208 + l = l.next; 1.209 + if (l != null && l.text.isEmpty()) 1.210 + lines.add(l); 1.211 + 1.212 + return lines; 1.213 + } 1.214 + } 1.215 + 1.216 + /** 1.217 + * An object to represent the comment that may precede the property 1.218 + * specification in a Message. 1.219 + * The comment is modelled as a list of fields, where the fields correspond 1.220 + * to the placeholder values (e.g. {0}, {1}, etc) within the message value. 1.221 + */ 1.222 + static final class Info { 1.223 + /** 1.224 + * An ordered set of descriptions for a placeholder value in a 1.225 + * message. 1.226 + */ 1.227 + static class Field { 1.228 + boolean unused; 1.229 + Set<String> values; 1.230 + boolean listOfAny = false; 1.231 + boolean setOfAny = false; 1.232 + Field(String s) { 1.233 + s = s.substring(s.indexOf(": ") + 2); 1.234 + values = new LinkedHashSet<String>(Arrays.asList(s.split(" or "))); 1.235 + for (String v: values) { 1.236 + if (v.startsWith("list of")) 1.237 + listOfAny = true; 1.238 + if (v.startsWith("set of")) 1.239 + setOfAny = true; 1.240 + } 1.241 + } 1.242 + 1.243 + /** 1.244 + * Return true if this field logically contains all the values of 1.245 + * another field. 1.246 + */ 1.247 + boolean contains(Field other) { 1.248 + if (unused != other.unused) 1.249 + return false; 1.250 + 1.251 + for (String v: other.values) { 1.252 + if (values.contains(v)) 1.253 + continue; 1.254 + if (v.equals("null") || v.equals("string")) 1.255 + continue; 1.256 + if (v.equals("list") && listOfAny) 1.257 + continue; 1.258 + if (v.equals("set") && setOfAny) 1.259 + continue; 1.260 + return false; 1.261 + } 1.262 + return true; 1.263 + } 1.264 + 1.265 + /** 1.266 + * Merge the values of another field into this field. 1.267 + */ 1.268 + void merge(Field other) { 1.269 + unused |= other.unused; 1.270 + values.addAll(other.values); 1.271 + 1.272 + // cleanup unnecessary entries 1.273 + 1.274 + if (values.contains("null") && values.size() > 1) { 1.275 + // "null" is superceded by anything else 1.276 + values.remove("null"); 1.277 + } 1.278 + 1.279 + if (values.contains("string") && values.size() > 1) { 1.280 + // "string" is superceded by anything else 1.281 + values.remove("string"); 1.282 + } 1.283 + 1.284 + if (values.contains("list")) { 1.285 + // list is superceded by "list of ..." 1.286 + for (String s: values) { 1.287 + if (s.startsWith("list of ")) { 1.288 + values.remove("list"); 1.289 + break; 1.290 + } 1.291 + } 1.292 + } 1.293 + 1.294 + if (values.contains("set")) { 1.295 + // set is superceded by "set of ..." 1.296 + for (String s: values) { 1.297 + if (s.startsWith("set of ")) { 1.298 + values.remove("set"); 1.299 + break; 1.300 + } 1.301 + } 1.302 + } 1.303 + 1.304 + if (other.values.contains("unused")) { 1.305 + values.clear(); 1.306 + values.add("unused"); 1.307 + } 1.308 + } 1.309 + 1.310 + void markUnused() { 1.311 + values = new LinkedHashSet<String>(); 1.312 + values.add("unused"); 1.313 + listOfAny = false; 1.314 + setOfAny = false; 1.315 + } 1.316 + 1.317 + @Override 1.318 + public String toString() { 1.319 + return values.toString(); 1.320 + } 1.321 + } 1.322 + 1.323 + /** The fields of the Info object. */ 1.324 + List<Field> fields = new ArrayList<Field>(); 1.325 + 1.326 + Info() { } 1.327 + 1.328 + Info(String text) throws IllegalArgumentException { 1.329 + if (!text.startsWith("# ")) 1.330 + throw new IllegalArgumentException(); 1.331 + String[] segs = text.substring(2).split(", "); 1.332 + fields = new ArrayList<Field>(); 1.333 + for (String seg: segs) { 1.334 + fields.add(new Field(seg)); 1.335 + } 1.336 + } 1.337 + 1.338 + Info(Set<String> infos) throws IllegalArgumentException { 1.339 + for (String s: infos) 1.340 + merge(new Info(s)); 1.341 + } 1.342 + 1.343 + boolean isEmpty() { 1.344 + return fields.isEmpty(); 1.345 + } 1.346 + 1.347 + boolean contains(Info other) { 1.348 + if (other.isEmpty()) 1.349 + return true; 1.350 + 1.351 + if (fields.size() != other.fields.size()) 1.352 + return false; 1.353 + 1.354 + Iterator<Field> oIter = other.fields.iterator(); 1.355 + for (Field values: fields) { 1.356 + if (!values.contains(oIter.next())) 1.357 + return false; 1.358 + } 1.359 + 1.360 + return true; 1.361 + } 1.362 + 1.363 + void merge(Info other) { 1.364 + if (fields.isEmpty()) { 1.365 + fields.addAll(other.fields); 1.366 + return; 1.367 + } 1.368 + 1.369 + if (other.fields.size() != fields.size()) 1.370 + throw new IllegalArgumentException(); 1.371 + 1.372 + Iterator<Field> oIter = other.fields.iterator(); 1.373 + for (Field d: fields) { 1.374 + d.merge(oIter.next()); 1.375 + } 1.376 + } 1.377 + 1.378 + void markUnused(Set<Integer> used) { 1.379 + for (int i = 0; i < fields.size(); i++) { 1.380 + if (!used.contains(i)) 1.381 + fields.get(i).markUnused(); 1.382 + } 1.383 + } 1.384 + 1.385 + @Override 1.386 + public String toString() { 1.387 + return fields.toString(); 1.388 + } 1.389 + 1.390 + String toComment() { 1.391 + StringBuilder sb = new StringBuilder(); 1.392 + sb.append("# "); 1.393 + String sep = ""; 1.394 + int i = 0; 1.395 + for (Field f: fields) { 1.396 + sb.append(sep); 1.397 + sb.append(i++); 1.398 + sb.append(": "); 1.399 + sep = ""; 1.400 + for (String s: f.values) { 1.401 + sb.append(sep); 1.402 + sb.append(s); 1.403 + sep = " or "; 1.404 + } 1.405 + sep = ", "; 1.406 + } 1.407 + return sb.toString(); 1.408 + } 1.409 + } 1.410 + 1.411 + Line firstLine; 1.412 + Line lastLine; 1.413 + Map<String, Message> messages = new TreeMap<String, Message>(); 1.414 + 1.415 + MessageFile(File file) throws IOException { 1.416 + Reader in = new FileReader(file); 1.417 + try { 1.418 + read(in); 1.419 + } finally { 1.420 + in.close(); 1.421 + } 1.422 + } 1.423 + 1.424 + MessageFile(Reader in) throws IOException { 1.425 + read(in); 1.426 + } 1.427 + 1.428 + final void read(Reader in) throws IOException { 1.429 + BufferedReader br = (in instanceof BufferedReader) 1.430 + ? (BufferedReader) in 1.431 + : new BufferedReader(in); 1.432 + String line; 1.433 + while ((line = br.readLine()) != null) { 1.434 + Line l; 1.435 + if (firstLine == null) 1.436 + l = firstLine = lastLine = new Line(line); 1.437 + else 1.438 + l = lastLine.insertAfter(line); 1.439 + if (line.startsWith("compiler.")) { 1.440 + int eq = line.indexOf("="); 1.441 + if (eq > 0) 1.442 + messages.put(line.substring(0, eq), new Message(l)); 1.443 + } 1.444 + } 1.445 + } 1.446 + 1.447 + void write(File file) throws IOException { 1.448 + Writer out = new FileWriter(file); 1.449 + try { 1.450 + write(out); 1.451 + } finally { 1.452 + out.close(); 1.453 + } 1.454 + } 1.455 + 1.456 + void write(Writer out) throws IOException { 1.457 + BufferedWriter bw = (out instanceof BufferedWriter) 1.458 + ? (BufferedWriter) out 1.459 + : new BufferedWriter(out); 1.460 + for (Line l = firstLine; l != null; l = l.next) { 1.461 + bw.write(l.text); 1.462 + bw.write("\n"); // always use Unix line endings 1.463 + } 1.464 + bw.flush(); 1.465 + } 1.466 +}