test/tools/javac/diags/MessageFile.java

Wed, 06 Apr 2011 20:33:44 -0700

author
ohair
date
Wed, 06 Apr 2011 20:33:44 -0700
changeset 962
0ff2bbd38f10
parent 842
3da26790ccb7
child 1657
f4500abff1fd
permissions
-rw-r--r--

7033660: Update copyright year to 2011 on any files changed in 2011
Reviewed-by: dholmes

     1 /*
     2  * Copyright (c) 2010, 2011, 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.
     8  *
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    12  * version 2 for more details (a copy is included in the LICENSE file that
    13  * accompanied this code).
    14  *
    15  * You should have received a copy of the GNU General Public License version
    16  * 2 along with this work; if not, write to the Free Software Foundation,
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    18  *
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    20  * or visit www.oracle.com if you need additional information or have any
    21  * questions.
    22  */
    24 import java.io.*;
    25 import java.util.*;
    26 import java.util.regex.Matcher;
    27 import java.util.regex.Pattern;
    29 /**
    30  * Class to facilitate manipulating compiler.properties.
    31  */
    32 class MessageFile {
    33     static final Pattern emptyOrCommentPattern = Pattern.compile("( *#.*)?");
    34     static final Pattern infoPattern = Pattern.compile("# ([0-9]+: [-A-Za-z ]+, )*[0-9]+: [-A-Za-z ]+");
    36     /**
    37      * A line of text within the message file.
    38      * The lines form a doubly linked list for simple navigation.
    39      */
    40     class Line {
    41         String text;
    42         Line prev;
    43         Line next;
    45         Line(String text) {
    46             this.text = text;
    47         }
    49         boolean isEmptyOrComment() {
    50             return emptyOrCommentPattern.matcher(text).matches();
    51         }
    53         boolean isInfo() {
    54             return infoPattern.matcher(text).matches();
    55         }
    57         boolean hasContinuation() {
    58             return (next != null) && text.endsWith("\\");
    59         }
    61         Line insertAfter(String text) {
    62             Line l = new Line(text);
    63             insertAfter(l);
    64             return l;
    65         }
    67         void insertAfter(Line l) {
    68             assert prev == null && next == null;
    69             l.prev = this;
    70             l.next = next;
    71             if (next == null)
    72                 lastLine = l;
    73             else
    74                 next.prev = l;
    75             next = l;
    76         }
    78         Line insertBefore(String text) {
    79             Line l = new Line(text);
    80             insertBefore(l);
    81             return l;
    82         }
    84         void insertBefore(Line l) {
    85             assert prev == null && next == null;
    86             l.prev = prev;
    87             l.next = this;
    88             if (prev == null)
    89                 firstLine = l;
    90             else
    91                 prev.next = l;
    92             prev = l;
    93         }
    95         void remove() {
    96             if (prev == null)
    97                 firstLine = next;
    98             else
    99                 prev.next = next;
   100             if (next == null)
   101                 lastLine = prev;
   102             else
   103                 next.prev = prev;
   104             prev = null;
   105             next = null;
   106         }
   107     }
   109     /**
   110      * A message within the message file.
   111      * A message is a series of lines containing a "name=value" property,
   112      * optionally preceded by a comment describing the use of placeholders
   113      * such as {0}, {1}, etc within the property value.
   114      */
   115     static final class Message {
   116         final Line firstLine;
   117         private Info info;
   119         Message(Line l) {
   120             firstLine = l;
   121         }
   123         boolean needInfo() {
   124             Line l = firstLine;
   125             while (true) {
   126                 if (l.text.matches(".*\\{[0-9]+\\}.*"))
   127                     return true;
   128                 if (!l.hasContinuation())
   129                     return false;
   130                 l = l.next;
   131             }
   132         }
   134         Set<Integer> getPlaceholders() {
   135             Pattern p = Pattern.compile("\\{([0-9]+)\\}");
   136             Set<Integer> results = new TreeSet<Integer>();
   137             Line l = firstLine;
   138             while (true) {
   139                 Matcher m = p.matcher(l.text);
   140                 while (m.find())
   141                     results.add(Integer.parseInt(m.group(1)));
   142                 if (!l.hasContinuation())
   143                     return results;
   144                 l = l.next;
   145             }
   146         }
   148         /**
   149          * Get the Info object for this message. It may be empty if there
   150          * if no comment preceding the property specification.
   151          */
   152         Info getInfo() {
   153             if (info == null) {
   154                 Line l = firstLine.prev;
   155                 if (l != null && l.isInfo())
   156                     info = new Info(l.text);
   157                 else
   158                     info = new Info();
   159             }
   160             return info;
   161         }
   163         /**
   164          * Set the Info for this message.
   165          * If there was an info comment preceding the property specification,
   166          * it will be updated; otherwise, one will be inserted.
   167          */
   168         void setInfo(Info info) {
   169             this.info = info;
   170             Line l = firstLine.prev;
   171             if (l != null && l.isInfo())
   172                 l.text = info.toComment();
   173             else
   174                 firstLine.insertBefore(info.toComment());
   175         }
   177         /**
   178          * Get all the lines pertaining to this message.
   179          */
   180         List<Line> getLines(boolean includeAllPrecedingComments) {
   181             List<Line> lines = new ArrayList<Line>();
   182             Line l = firstLine;
   183             if (includeAllPrecedingComments) {
   184                 // scan back to find end of prev message
   185                 while (l.prev != null && l.prev.isEmptyOrComment())
   186                     l = l.prev;
   187                 // skip leading blank lines
   188                 while (l.text.isEmpty())
   189                     l = l.next;
   190             } else {
   191                 if (l.prev != null && l.prev.isInfo())
   192                     l = l.prev;
   193             }
   195             // include any preceding lines
   196             for ( ; l != firstLine; l = l.next)
   197                 lines.add(l);
   199             // include message lines
   200             for (l = firstLine; l != null && l.hasContinuation(); l = l.next)
   201                 lines.add(l);
   202             lines.add(l);
   204             // include trailing blank line if present
   205             l = l.next;
   206             if (l != null && l.text.isEmpty())
   207                 lines.add(l);
   209             return lines;
   210         }
   211     }
   213     /**
   214      * An object to represent the comment that may precede the property
   215      * specification in a Message.
   216      * The comment is modelled as a list of fields, where the fields correspond
   217      * to the placeholder values (e.g. {0}, {1}, etc) within the message value.
   218      */
   219     static final class Info {
   220         /**
   221          * An ordered set of descriptions for a placeholder value in a
   222          * message.
   223          */
   224         static class Field {
   225             boolean unused;
   226             Set<String> values;
   227             boolean listOfAny = false;
   228             boolean setOfAny = false;
   229             Field(String s) {
   230                 s = s.substring(s.indexOf(": ") + 2);
   231                 values = new LinkedHashSet<String>(Arrays.asList(s.split(" or ")));
   232                 for (String v: values) {
   233                     if (v.startsWith("list of"))
   234                         listOfAny = true;
   235                     if (v.startsWith("set of"))
   236                         setOfAny = true;
   237                 }
   238             }
   240             /**
   241              * Return true if this field logically contains all the values of
   242              * another field.
   243              */
   244             boolean contains(Field other) {
   245                 if (unused != other.unused)
   246                     return false;
   248                 for (String v: other.values) {
   249                     if (values.contains(v))
   250                         continue;
   251                     if (v.equals("null") || v.equals("string"))
   252                         continue;
   253                     if (v.equals("list") && listOfAny)
   254                         continue;
   255                     if (v.equals("set") && setOfAny)
   256                         continue;
   257                     return false;
   258                 }
   259                 return true;
   260             }
   262             /**
   263              * Merge the values of another field into this field.
   264              */
   265             void merge(Field other) {
   266                 unused |= other.unused;
   267                 values.addAll(other.values);
   269                 // cleanup unnecessary entries
   271                 if (values.contains("null") && values.size() > 1) {
   272                     // "null" is superceded by anything else
   273                     values.remove("null");
   274                 }
   276                 if (values.contains("string") && values.size() > 1) {
   277                     // "string" is superceded by anything else
   278                     values.remove("string");
   279                 }
   281                 if (values.contains("list")) {
   282                     // list is superceded by "list of ..."
   283                     for (String s: values) {
   284                         if (s.startsWith("list of ")) {
   285                             values.remove("list");
   286                             break;
   287                         }
   288                     }
   289                 }
   291                 if (values.contains("set")) {
   292                     // set is superceded by "set of ..."
   293                     for (String s: values) {
   294                         if (s.startsWith("set of ")) {
   295                             values.remove("set");
   296                             break;
   297                         }
   298                     }
   299                 }
   301                 if (other.values.contains("unused")) {
   302                     values.clear();
   303                     values.add("unused");
   304                 }
   305             }
   307             void markUnused() {
   308                 values = new LinkedHashSet<String>();
   309                 values.add("unused");
   310                 listOfAny = false;
   311                 setOfAny = false;
   312             }
   314             @Override
   315             public String toString() {
   316                 return values.toString();
   317             }
   318         }
   320         /** The fields of the Info object. */
   321         List<Field> fields = new ArrayList<Field>();
   323         Info() { }
   325         Info(String text) throws IllegalArgumentException {
   326             if (!text.startsWith("# "))
   327                 throw new IllegalArgumentException();
   328             String[] segs = text.substring(2).split(", ");
   329             fields = new ArrayList<Field>();
   330             for (String seg: segs) {
   331                 fields.add(new Field(seg));
   332             }
   333         }
   335         Info(Set<String> infos) throws IllegalArgumentException {
   336             for (String s: infos)
   337                 merge(new Info(s));
   338         }
   340         boolean isEmpty() {
   341             return fields.isEmpty();
   342         }
   344         boolean contains(Info other) {
   345             if (other.isEmpty())
   346                 return true;
   348             if (fields.size() != other.fields.size())
   349                 return false;
   351             Iterator<Field> oIter = other.fields.iterator();
   352             for (Field values: fields) {
   353                 if (!values.contains(oIter.next()))
   354                     return false;
   355             }
   357             return true;
   358         }
   360         void merge(Info other) {
   361             if (fields.isEmpty()) {
   362                 fields.addAll(other.fields);
   363                 return;
   364             }
   366             if (other.fields.size() != fields.size())
   367                 throw new IllegalArgumentException();
   369             Iterator<Field> oIter = other.fields.iterator();
   370             for (Field d: fields) {
   371                 d.merge(oIter.next());
   372             }
   373         }
   375         void markUnused(Set<Integer> used) {
   376             for (int i = 0; i < fields.size(); i++) {
   377                 if (!used.contains(i))
   378                     fields.get(i).markUnused();
   379             }
   380         }
   382         @Override
   383         public String toString() {
   384             return fields.toString();
   385         }
   387         String toComment() {
   388             StringBuilder sb = new StringBuilder();
   389             sb.append("# ");
   390             String sep = "";
   391             int i = 0;
   392             for (Field f: fields) {
   393                 sb.append(sep);
   394                 sb.append(i++);
   395                 sb.append(": ");
   396                 sep = "";
   397                 for (String s: f.values) {
   398                     sb.append(sep);
   399                     sb.append(s);
   400                     sep = " or ";
   401                 }
   402                 sep = ", ";
   403             }
   404             return sb.toString();
   405         }
   406     }
   408     Line firstLine;
   409     Line lastLine;
   410     Map<String, Message> messages = new TreeMap<String, Message>();
   412     MessageFile(File file) throws IOException {
   413         Reader in = new FileReader(file);
   414         try {
   415             read(in);
   416         } finally {
   417             in.close();
   418         }
   419     }
   421     MessageFile(Reader in) throws IOException {
   422         read(in);
   423     }
   425     final void read(Reader in) throws IOException {
   426         BufferedReader br = (in instanceof BufferedReader)
   427                 ? (BufferedReader) in
   428                 : new BufferedReader(in);
   429         String line;
   430         while ((line = br.readLine()) != null) {
   431             Line l;
   432             if (firstLine == null)
   433                 l = firstLine = lastLine = new Line(line);
   434             else
   435                 l = lastLine.insertAfter(line);
   436             if (line.startsWith("compiler.")) {
   437                 int eq = line.indexOf("=");
   438                 if (eq > 0)
   439                     messages.put(line.substring(0, eq), new Message(l));
   440             }
   441         }
   442     }
   444     void write(File file) throws IOException {
   445         Writer out = new FileWriter(file);
   446         try {
   447             write(out);
   448         } finally {
   449             out.close();
   450         }
   451     }
   453     void write(Writer out) throws IOException {
   454         BufferedWriter bw = (out instanceof BufferedWriter)
   455                 ? (BufferedWriter) out
   456                 : new BufferedWriter(out);
   457         for (Line l = firstLine; l != null; l = l.next) {
   458             bw.write(l.text);
   459             bw.write("\n"); // always use Unix line endings
   460         }
   461         bw.flush();
   462     }
   463 }

mercurial