001    /*
002     * Copyright 2004 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    package com.sun.tools.apt.mirror.declaration;
027    
028    
029    import java.util.Collection;
030    
031    import com.sun.mirror.declaration.*;
032    import com.sun.mirror.type.TypeMirror;
033    import com.sun.tools.apt.mirror.type.TypeMirrorImpl;
034    import com.sun.tools.javac.code.Type;
035    
036    import static com.sun.tools.javac.code.TypeTags.*;
037    
038    
039    /**
040     * Utility class for operating on constant expressions.
041     */
042    class Constants {
043    
044        /**
045         * Converts a constant in javac's internal representation (in which
046         * boolean, char, byte, short, and int are each represented by an Integer)
047         * into standard representation.  Other values (including null) are
048         * returned unchanged.
049         */
050        static Object decodeConstant(Object value, Type type) {
051            if (value instanceof Integer) {
052                int i = ((Integer) value).intValue();
053                switch (type.tag) {
054                case BOOLEAN:  return Boolean.valueOf(i != 0);
055                case CHAR:     return Character.valueOf((char) i);
056                case BYTE:     return Byte.valueOf((byte) i);
057                case SHORT:    return Short.valueOf((short) i);
058                }
059            }
060            return value;
061        }
062    
063        /**
064         * Returns a formatter for generating the text of constant
065         * expressions.  Equivalent to
066         * <tt>getFormatter(new StringBuilder())</tt>.
067         */
068        static Formatter getFormatter() {
069            return new Formatter(new StringBuilder());
070        }
071    
072        /**
073         * Returns a formatter for generating the text of constant
074         * expressions.  Also generates the text of constant
075         * "pseudo-expressions" for annotations and array-valued
076         * annotation elements.
077         *
078         * @param buf  where the expression is written
079         */
080        static Formatter getFormatter(StringBuilder buf) {
081            return new Formatter(buf);
082        }
083    
084    
085        /**
086         * Utility class used to generate the text of constant
087         * expressions.  Also generates the text of constant
088         * "pseudo-expressions" for annotations and array-valued
089         * annotation elements.
090         */
091        static class Formatter {
092    
093            private StringBuilder buf;      // where the output goes
094    
095            private Formatter(StringBuilder buf) {
096                this.buf = buf;
097            }
098    
099    
100            public String toString() {
101                return buf.toString();
102            }
103    
104            /**
105             * Appends a constant whose type is not statically known
106             * by dispatching to the appropriate overloaded append method.
107             */
108            void append(Object val) {
109                if (val instanceof String) {
110                    append((String) val);
111                } else if (val instanceof Character) {
112                    append((Character) val);
113                } else if (val instanceof Boolean) {
114                    append((Boolean) val);
115                } else if (val instanceof Byte) {
116                    append((Byte) val);
117                } else if (val instanceof Short) {
118                    append((Short) val);
119                } else if (val instanceof Integer) {
120                    append((Integer) val);
121                } else if (val instanceof Long) {
122                    append((Long) val);
123                } else if (val instanceof Float) {
124                    append((Float) val);
125                } else if (val instanceof Double) {
126                    append((Double) val);
127                } else if (val instanceof TypeMirror) {
128                    append((TypeMirrorImpl) val);
129                } else if (val instanceof EnumConstantDeclaration) {
130                    append((EnumConstantDeclarationImpl) val);
131                } else if (val instanceof AnnotationMirror) {
132                    append((AnnotationMirrorImpl) val);
133                } else if (val instanceof Collection<?>) {
134                    append((Collection<?>) val);
135                } else {
136                    appendUnquoted(val.toString());
137                }
138            }
139    
140            /**
141             * Appends a string, escaped (as needed) and quoted.
142             */
143            void append(String val) {
144                buf.append('"');
145                appendUnquoted(val);
146                buf.append('"');
147            }
148    
149            /**
150             * Appends a Character, escaped (as needed) and quoted.
151             */
152            void append(Character val) {
153                buf.append('\'');
154                appendUnquoted(val.charValue());
155                buf.append('\'');
156            }
157    
158            void append(Boolean val) {
159                buf.append(val);
160            }
161    
162            void append(Byte val) {
163                buf.append(String.format("0x%02x", val));
164            }
165    
166            void append(Short val) {
167                buf.append(val);
168            }
169    
170            void append(Integer val) {
171                buf.append(val);
172            }
173    
174            void append(Long val) {
175                buf.append(val).append('L');
176            }
177    
178            void append(Float val) {
179                if (val.isNaN()) {
180                    buf.append("0.0f/0.0f");
181                } else if (val.isInfinite()) {
182                    if (val.floatValue() < 0) {
183                        buf.append('-');
184                    }
185                    buf.append("1.0f/0.0f");
186                } else {
187                    buf.append(val).append('f');
188                }
189            }
190    
191            void append(Double val) {
192                if (val.isNaN()) {
193                    buf.append("0.0/0.0");
194                } else if (val.isInfinite()) {
195                    if (val.doubleValue() < 0) {
196                        buf.append('-');
197                    }
198                    buf.append("1.0/0.0");
199                } else {
200                    buf.append(val);
201                }
202            }
203    
204            /**
205             * Appends the class literal corresponding to a type.  Should
206             * only be invoked for types that have an associated literal.
207             * e.g:  "java.lang.String.class"
208             *       "boolean.class"
209             *       "int[].class"
210             */
211            void append(TypeMirrorImpl t) {
212                appendUnquoted(t.type.toString());
213                buf.append(".class");
214            }
215    
216            /**
217             * Appends the fully qualified name of an enum constant.
218             * e.g:  "java.math.RoundingMode.UP"
219             */
220            void append(EnumConstantDeclarationImpl e) {
221                appendUnquoted(e.sym.enclClass() + "." + e);
222            }
223    
224            /**
225             * Appends the text of an annotation pseudo-expression.
226             * e.g:  "@pkg.Format(linesep='\n')"
227             */
228            void append(AnnotationMirrorImpl anno) {
229                appendUnquoted(anno.toString());
230            }
231    
232            /**
233             * Appends the elements of a collection, enclosed within braces
234             * and separated by ", ".  Useful for array-valued annotation
235             * elements.
236             */
237            void append(Collection<?> vals) {
238                buf.append('{');
239                boolean first = true;
240                for (Object val : vals) {
241                    if (first) {
242                        first = false;
243                    } else {
244                        buf.append(", ");
245                    }
246                    append(((AnnotationValue) val).getValue());
247                }
248                buf.append('}');
249            }
250    
251    
252            /**
253             * For each char of a string, append using appendUnquoted(char).
254             */
255            private void appendUnquoted(String s) {
256                for (char c : s.toCharArray()) {
257                    appendUnquoted(c);
258                }
259            }
260    
261            /**
262             * Appends a char (unquoted), using escapes for those that are not
263             * printable ASCII.  We don't know what is actually printable in
264             * the locale in which this result will be used, so ASCII is our
265             * best guess as to the least common denominator.
266             */
267            private void appendUnquoted(char c) {
268                switch (c) {
269                case '\b': buf.append("\\b");  break;
270                case '\t': buf.append("\\t");  break;
271                case '\n': buf.append("\\n");  break;
272                case '\f': buf.append("\\f");  break;
273                case '\r': buf.append("\\r");  break;
274                case '\"': buf.append("\\\""); break;
275                case '\'': buf.append("\\\'"); break;
276                case '\\': buf.append("\\\\"); break;
277                default:
278                    if (isPrintableAscii(c)) {
279                        buf.append(c);
280                    } else {
281                        buf.append(String.format("\\u%04x", (int) c));
282                    }
283                }
284            }
285    
286            /**
287             * Is c a printable ASCII character?
288             */
289            private static boolean isPrintableAscii(char c) {
290                return c >= ' ' && c <= '~';
291            }
292        }
293    }