1 /*
   2  * Copyright 2004 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package com.sun.tools.apt.mirror.declaration;
  27 
  28 
  29 import java.util.Collection;
  30 
  31 import com.sun.mirror.declaration.*;
  32 import com.sun.mirror.type.TypeMirror;
  33 import com.sun.tools.apt.mirror.type.TypeMirrorImpl;
  34 import com.sun.tools.javac.code.Type;
  35 
  36 import static com.sun.tools.javac.code.TypeTags.*;
  37 
  38 
  39 /**
  40  * Utility class for operating on constant expressions.
  41  */
  42 @SuppressWarnings("deprecation")
  43 class Constants {
  44 
  45     /**
  46      * Converts a constant in javac's internal representation (in which
  47      * boolean, char, byte, short, and int are each represented by an Integer)
  48      * into standard representation.  Other values (including null) are
  49      * returned unchanged.
  50      */
  51     static Object decodeConstant(Object value, Type type) {
  52         if (value instanceof Integer) {
  53             int i = ((Integer) value).intValue();
  54             switch (type.tag) {
  55             case BOOLEAN:  return Boolean.valueOf(i != 0);
  56             case CHAR:     return Character.valueOf((char) i);
  57             case BYTE:     return Byte.valueOf((byte) i);
  58             case SHORT:    return Short.valueOf((short) i);
  59             }
  60         }
  61         return value;
  62     }
  63 
  64     /**
  65      * Returns a formatter for generating the text of constant
  66      * expressions.  Equivalent to
  67      * <tt>getFormatter(new StringBuilder())</tt>.
  68      */
  69     static Formatter getFormatter() {
  70         return new Formatter(new StringBuilder());
  71     }
  72 
  73     /**
  74      * Returns a formatter for generating the text of constant
  75      * expressions.  Also generates the text of constant
  76      * "pseudo-expressions" for annotations and array-valued
  77      * annotation elements.
  78      *
  79      * @param buf  where the expression is written
  80      */
  81     static Formatter getFormatter(StringBuilder buf) {
  82         return new Formatter(buf);
  83     }
  84 
  85 
  86     /**
  87      * Utility class used to generate the text of constant
  88      * expressions.  Also generates the text of constant
  89      * "pseudo-expressions" for annotations and array-valued
  90      * annotation elements.
  91      */
  92     static class Formatter {
  93 
  94         private StringBuilder buf;      // where the output goes
  95 
  96         private Formatter(StringBuilder buf) {
  97             this.buf = buf;
  98         }
  99 
 100 
 101         public String toString() {
 102             return buf.toString();
 103         }
 104 
 105         /**
 106          * Appends a constant whose type is not statically known
 107          * by dispatching to the appropriate overloaded append method.
 108          */
 109         void append(Object val) {
 110             if (val instanceof String) {
 111                 append((String) val);
 112             } else if (val instanceof Character) {
 113                 append((Character) val);
 114             } else if (val instanceof Boolean) {
 115                 append((Boolean) val);
 116             } else if (val instanceof Byte) {
 117                 append((Byte) val);
 118             } else if (val instanceof Short) {
 119                 append((Short) val);
 120             } else if (val instanceof Integer) {
 121                 append((Integer) val);
 122             } else if (val instanceof Long) {
 123                 append((Long) val);
 124             } else if (val instanceof Float) {
 125                 append((Float) val);
 126             } else if (val instanceof Double) {
 127                 append((Double) val);
 128             } else if (val instanceof TypeMirror) {
 129                 append((TypeMirrorImpl) val);
 130             } else if (val instanceof EnumConstantDeclaration) {
 131                 append((EnumConstantDeclarationImpl) val);
 132             } else if (val instanceof AnnotationMirror) {
 133                 append((AnnotationMirrorImpl) val);
 134             } else if (val instanceof Collection<?>) {
 135                 append((Collection<?>) val);
 136             } else {
 137                 appendUnquoted(val.toString());
 138             }
 139         }
 140 
 141         /**
 142          * Appends a string, escaped (as needed) and quoted.
 143          */
 144         void append(String val) {
 145             buf.append('"');
 146             appendUnquoted(val);
 147             buf.append('"');
 148         }
 149 
 150         /**
 151          * Appends a Character, escaped (as needed) and quoted.
 152          */
 153         void append(Character val) {
 154             buf.append('\'');
 155             appendUnquoted(val.charValue());
 156             buf.append('\'');
 157         }
 158 
 159         void append(Boolean val) {
 160             buf.append(val);
 161         }
 162 
 163         void append(Byte val) {
 164             buf.append(String.format("0x%02x", val));
 165         }
 166 
 167         void append(Short val) {
 168             buf.append(val);
 169         }
 170 
 171         void append(Integer val) {
 172             buf.append(val);
 173         }
 174 
 175         void append(Long val) {
 176             buf.append(val).append('L');
 177         }
 178 
 179         void append(Float val) {
 180             if (val.isNaN()) {
 181                 buf.append("0.0f/0.0f");
 182             } else if (val.isInfinite()) {
 183                 if (val.floatValue() < 0) {
 184                     buf.append('-');
 185                 }
 186                 buf.append("1.0f/0.0f");
 187             } else {
 188                 buf.append(val).append('f');
 189             }
 190         }
 191 
 192         void append(Double val) {
 193             if (val.isNaN()) {
 194                 buf.append("0.0/0.0");
 195             } else if (val.isInfinite()) {
 196                 if (val.doubleValue() < 0) {
 197                     buf.append('-');
 198                 }
 199                 buf.append("1.0/0.0");
 200             } else {
 201                 buf.append(val);
 202             }
 203         }
 204 
 205         /**
 206          * Appends the class literal corresponding to a type.  Should
 207          * only be invoked for types that have an associated literal.
 208          * e.g:  "java.lang.String.class"
 209          *       "boolean.class"
 210          *       "int[].class"
 211          */
 212         void append(TypeMirrorImpl t) {
 213             appendUnquoted(t.type.toString());
 214             buf.append(".class");
 215         }
 216 
 217         /**
 218          * Appends the fully qualified name of an enum constant.
 219          * e.g:  "java.math.RoundingMode.UP"
 220          */
 221         void append(EnumConstantDeclarationImpl e) {
 222             appendUnquoted(e.sym.enclClass() + "." + e);
 223         }
 224 
 225         /**
 226          * Appends the text of an annotation pseudo-expression.
 227          * e.g:  "@pkg.Format(linesep='\n')"
 228          */
 229         void append(AnnotationMirrorImpl anno) {
 230             appendUnquoted(anno.toString());
 231         }
 232 
 233         /**
 234          * Appends the elements of a collection, enclosed within braces
 235          * and separated by ", ".  Useful for array-valued annotation
 236          * elements.
 237          */
 238         void append(Collection<?> vals) {
 239             buf.append('{');
 240             boolean first = true;
 241             for (Object val : vals) {
 242                 if (first) {
 243                     first = false;
 244                 } else {
 245                     buf.append(", ");
 246                 }
 247                 append(((AnnotationValue) val).getValue());
 248             }
 249             buf.append('}');
 250         }
 251 
 252 
 253         /**
 254          * For each char of a string, append using appendUnquoted(char).
 255          */
 256         private void appendUnquoted(String s) {
 257             for (char c : s.toCharArray()) {
 258                 appendUnquoted(c);
 259             }
 260         }
 261 
 262         /**
 263          * Appends a char (unquoted), using escapes for those that are not
 264          * printable ASCII.  We don't know what is actually printable in
 265          * the locale in which this result will be used, so ASCII is our
 266          * best guess as to the least common denominator.
 267          */
 268         private void appendUnquoted(char c) {
 269             switch (c) {
 270             case '\b': buf.append("\\b");  break;
 271             case '\t': buf.append("\\t");  break;
 272             case '\n': buf.append("\\n");  break;
 273             case '\f': buf.append("\\f");  break;
 274             case '\r': buf.append("\\r");  break;
 275             case '\"': buf.append("\\\""); break;
 276             case '\'': buf.append("\\\'"); break;
 277             case '\\': buf.append("\\\\"); break;
 278             default:
 279                 if (isPrintableAscii(c)) {
 280                     buf.append(c);
 281                 } else {
 282                     buf.append(String.format("\\u%04x", (int) c));
 283                 }
 284             }
 285         }
 286 
 287         /**
 288          * Is c a printable ASCII character?
 289          */
 290         private static boolean isPrintableAscii(char c) {
 291             return c >= ' ' && c <= '~';
 292         }
 293     }
 294 }