001    /*
002     * Copyright 2003-2008 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.javac.util;
027    
028    import java.util.Locale;
029    import java.util.Map;
030    
031    import javax.tools.Diagnostic;
032    import javax.tools.JavaFileObject;
033    
034    import com.sun.tools.javac.api.DiagnosticFormatter;
035    import com.sun.tools.javac.file.JavacFileManager;
036    import com.sun.tools.javac.tree.JCTree;
037    
038    import static com.sun.tools.javac.util.JCDiagnostic.DiagnosticType.*;
039    
040    /** An abstraction of a diagnostic message generated by the compiler.
041     *
042     *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
043     *  you write code that depends on this, you do so at your own risk.
044     *  This code and its internal interfaces are subject to change or
045     *  deletion without notice.</b>
046     */
047    public class JCDiagnostic implements Diagnostic<JavaFileObject> {
048        /** A factory for creating diagnostic objects. */
049        public static class Factory {
050            /** The context key for the diagnostic factory. */
051            protected static final Context.Key<JCDiagnostic.Factory> diagnosticFactoryKey =
052                new Context.Key<JCDiagnostic.Factory>();
053    
054            /** Get the Factory instance for this context. */
055            public static Factory instance(Context context) {
056                Factory instance = context.get(diagnosticFactoryKey);
057                if (instance == null)
058                    instance = new Factory(context);
059                return instance;
060            }
061    
062            DiagnosticFormatter<JCDiagnostic> formatter;
063            final String prefix;
064    
065            /** Create a new diagnostic factory. */
066            protected Factory(Context context) {
067                this(JavacMessages.instance(context), "compiler");
068                context.put(diagnosticFactoryKey, this);
069            }
070    
071            /** Create a new diagnostic factory. */
072            public Factory(JavacMessages messages, String prefix) {
073                this.prefix = prefix;
074                this.formatter = new BasicDiagnosticFormatter(messages);
075            }
076    
077            /**
078             * Create an error diagnostic.
079             *  @param source The source of the compilation unit, if any, in which to report the error.
080             *  @param pos    The source position at which to report the error.
081             *  @param key    The key for the localized error message.
082             *  @param args   Fields of the error message.
083             */
084            public JCDiagnostic error(
085                    DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
086                return new JCDiagnostic(formatter, ERROR, true, source, pos, qualify(ERROR, key), args);
087            }
088    
089            /**
090             * Create a warning diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
091             *  @param source The source of the compilation unit, if any, in which to report the warning.
092             *  @param pos    The source position at which to report the warning.
093             *  @param key    The key for the localized error message.
094             *  @param args   Fields of the error message.
095             *  @see MandatoryWarningHandler
096             */
097            public JCDiagnostic mandatoryWarning(
098                     DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
099                return new JCDiagnostic(formatter, WARNING, true, source, pos, qualify(WARNING, key), args);
100            }
101    
102            /**
103             * Create a warning diagnostic.
104             *  @param source The source of the compilation unit, if any, in which to report the warning.
105             *  @param pos    The source position at which to report the warning.
106             *  @param key    The key for the localized error message.
107             *  @param args   Fields of the error message.
108             */
109            public JCDiagnostic warning(
110                    DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
111                return new JCDiagnostic(formatter, WARNING, false, source, pos, qualify(WARNING, key), args);
112            }
113    
114            /**
115             * Create a note diagnostic that will not be hidden by the -nowarn or -Xlint:none options.
116             *  @param key    The key for the localized error message.
117             *  @param args   Fields of the error message.
118             *  @see MandatoryWarningHandler
119             */
120            public JCDiagnostic mandatoryNote(DiagnosticSource source, String key, Object... args) {
121                return new JCDiagnostic(formatter, NOTE, true, source, null, qualify(NOTE, key), args);
122            }
123    
124            /**
125             * Create a note diagnostic.
126             *  @param key    The key for the localized error message.
127             *  @param args   Fields of the error message.
128             */
129            public JCDiagnostic note(String key, Object... args) {
130                return note(null, null, key, args);
131            }
132    
133            /**
134             * Create a note diagnostic.
135             *  @param source The source of the compilation unit, if any, in which to report the note.
136             *  @param pos    The source position at which to report the note.
137             *  @param key    The key for the localized error message.
138             *  @param args   Fields of the error message.
139             */
140            public JCDiagnostic note(
141                    DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
142                return new JCDiagnostic(formatter, NOTE, false, source, pos, qualify(NOTE, key), args);
143            }
144    
145            /**
146             * Create a fragment diagnostic, for use as an argument in other diagnostics
147             *  @param key    The key for the localized error message.
148             *  @param args   Fields of the error message.
149             */
150            public JCDiagnostic fragment(String key, Object... args) {
151                return new JCDiagnostic(formatter, FRAGMENT, false, null, null, qualify(FRAGMENT, key), args);
152            }
153    
154            protected String qualify(DiagnosticType t, String key) {
155                return prefix + "." + t.key + "." + key;
156            }
157        }
158    
159    
160    
161        /**
162         * Create a fragment diagnostic, for use as an argument in other diagnostics
163         *  @param key    The key for the localized error message.
164         *  @param args   Fields of the error message.
165         *
166         */
167        @Deprecated
168        public static JCDiagnostic fragment(String key, Object... args) {
169            return new JCDiagnostic(getFragmentFormatter(),
170                                  FRAGMENT,
171                                  false,
172                                  null,
173                                  null,
174                                  "compiler." + FRAGMENT.key + "." + key,
175                                  args);
176        }
177        //where
178        @Deprecated
179        public static DiagnosticFormatter<JCDiagnostic> getFragmentFormatter() {
180            if (fragmentFormatter == null) {
181                fragmentFormatter = new BasicDiagnosticFormatter(JavacMessages.getDefaultMessages());
182            }
183            return fragmentFormatter;
184        }
185    
186        /**
187         * A DiagnosticType defines the type of the diagnostic.
188         **/
189        public enum DiagnosticType {
190            /** A fragment of an enclosing diagnostic. */
191            FRAGMENT("misc"),
192            /** A note: similar to, but less serious than, a warning. */
193            NOTE("note"),
194            /** A warning. */
195            WARNING("warn"),
196            /** An error. */
197            ERROR("err");
198    
199            final String key;
200    
201            /** Create a DiagnosticType.
202             * @param key A string used to create the resource key for the diagnostic.
203             */
204            DiagnosticType(String key) {
205                this.key = key;
206            }
207        };
208    
209        /**
210         * A DiagnosticPosition provides information about the positions in a file
211         * that gave rise to a diagnostic. It always defines a "preferred" position
212         * that most accurately defines the location of the diagnostic, it may also
213         * provide a related tree node that spans that location.
214         */
215        public static interface DiagnosticPosition {
216            /** Gets the tree node, if any, to which the diagnostic applies. */
217            JCTree getTree();
218            /** If there is a tree node, get the start position of the tree node.
219             *  Otherwise, just returns the same as getPreferredPosition(). */
220            int getStartPosition();
221            /** Get the position within the file that most accurately defines the
222             *  location for the diagnostic. */
223            int getPreferredPosition();
224            /** If there is a tree node, and if endPositions are available, get
225             *  the end position of the tree node. Otherwise, just returns the
226             *  same as getPreferredPosition(). */
227            int getEndPosition(Map<JCTree, Integer> endPosTable);
228        }
229    
230        /**
231         * A DiagnosticPosition that simply identifies a position, but no related
232         * tree node, as the location for a diagnostic. Used for scanner and parser
233         * diagnostics. */
234        public static class SimpleDiagnosticPosition implements DiagnosticPosition {
235            public SimpleDiagnosticPosition(int pos) {
236                this.pos = pos;
237            }
238    
239            public JCTree getTree() {
240                return null;
241            }
242    
243            public int getStartPosition() {
244                return pos;
245            }
246    
247            public int getPreferredPosition() {
248                return pos;
249            }
250    
251            public int getEndPosition(Map<JCTree, Integer> endPosTable) {
252                return pos;
253            }
254    
255            private final int pos;
256        }
257    
258        private final DiagnosticType type;
259        private final DiagnosticSource source;
260        private final DiagnosticPosition position;
261        private final int line;
262        private final int column;
263        private final String key;
264        protected Object[] args;
265        private boolean mandatory;
266    
267        /**
268         * Create a diagnostic object.
269         * @param messages the resource for localized messages
270         * @param dt the type of diagnostic
271         * @param name the name of the source file, or null if none.
272         * @param pos the character offset within the source file, if given.
273         * @param key a resource key to identify the text of the diagnostic
274         * @param args arguments to be included in the text of the diagnostic
275         */
276        protected JCDiagnostic(DiagnosticFormatter<JCDiagnostic> formatter,
277                           DiagnosticType dt,
278                           boolean mandatory,
279                           DiagnosticSource source,
280                           DiagnosticPosition pos,
281                           String key,
282                           Object ... args) {
283            if (source == null && pos != null && pos.getPreferredPosition() != Position.NOPOS)
284                throw new IllegalArgumentException();
285    
286            this.defaultFormatter = formatter;
287            this.type = dt;
288            this.mandatory = mandatory;
289            this.source = source;
290            this.position = pos;
291            this.key = key;
292                this.args = args;
293    
294            int n = (pos == null ? Position.NOPOS : pos.getPreferredPosition());
295            if (n == Position.NOPOS || source == null)
296                line = column = -1;
297            else {
298                line = source.getLineNumber(n);
299                column = source.getColumnNumber(n, true);
300            }
301        }
302    
303        /**
304         * Get the type of this diagnostic.
305         * @return the type of this diagnostic
306         */
307        public DiagnosticType getType() {
308            return type;
309        }
310    
311        /**
312         * Get the subdiagnostic list
313         * @return subdiagnostic list
314         */
315        public List<JCDiagnostic> getSubdiagnostics() {
316            return List.nil();
317        }
318    
319        public boolean isMultiline() {
320            return false;
321        }
322    
323        /**
324         * Check whether or not this diagnostic is required to be shown.
325         * @return true if this diagnostic is required to be shown.
326         */
327        public boolean isMandatory() {
328            return mandatory;
329        }
330    
331        /**
332         * Get the name of the source file referred to by this diagnostic.
333         * @return the name of the source referred to with this diagnostic, or null if none
334         */
335        public JavaFileObject getSource() {
336            if (source == null)
337                return null;
338            else
339                return source.getFile();
340        }
341    
342        /**
343         * Get the name of the source file referred to by this diagnostic.
344         * @return the name of the source referred to with this diagnostic, or null if none
345         */
346        public String getSourceName() {
347            JavaFileObject s = getSource();
348            return s == null ? null : JavacFileManager.getJavacFileName(s);
349        }
350    
351        /**
352         * Get the source referred to by this diagnostic.
353         * @return the source referred to with this diagnostic, or null if none
354         */
355        public DiagnosticSource getDiagnosticSource() {
356            return source;
357        }
358    
359        protected int getIntStartPosition() {
360            return (position == null ? Position.NOPOS : position.getStartPosition());
361        }
362    
363        protected int getIntPosition() {
364            return (position == null ? Position.NOPOS : position.getPreferredPosition());
365        }
366    
367        protected int getIntEndPosition() {
368            return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable()));
369        }
370    
371        public long getStartPosition() {
372            return getIntStartPosition();
373        }
374    
375        public long getPosition() {
376            return getIntPosition();
377        }
378    
379        public long getEndPosition() {
380            return getIntEndPosition();
381        }
382    
383        /**
384         * Get the line number within the source referred to by this diagnostic.
385         * @return  the line number within the source referred to by this diagnostic
386         */
387        public long getLineNumber() {
388            return line;
389        }
390    
391        /**
392         * Get the column number within the line of source referred to by this diagnostic.
393         * @return  the column number within the line of source referred to by this diagnostic
394         */
395        public long getColumnNumber() {
396            return column;
397        }
398    
399        /**
400         * Get the arguments to be included in the text of the diagnostic.
401         * @return  the arguments to be included in the text of the diagnostic
402         */
403        public Object[] getArgs() {
404            return args;
405        }
406    
407        /**
408         * Get the prefix string associated with this type of diagnostic.
409         * @return the prefix string associated with this type of diagnostic
410         */
411        public String getPrefix() {
412            return getPrefix(type);
413        }
414    
415        /**
416         * Get the prefix string associated with a particular type of diagnostic.
417         * @return the prefix string associated with a particular type of diagnostic
418         */
419        public String getPrefix(DiagnosticType dt) {
420            return defaultFormatter.formatKind(this, Locale.getDefault());
421        }
422    
423        /**
424         * Return the standard presentation of this diagnostic.
425         */
426        public String toString() {
427            return defaultFormatter.format(this,Locale.getDefault());
428        }
429    
430        private DiagnosticFormatter<JCDiagnostic> defaultFormatter;
431        @Deprecated
432        private static DiagnosticFormatter<JCDiagnostic> fragmentFormatter;
433    
434        // Methods for javax.tools.Diagnostic
435    
436        public Diagnostic.Kind getKind() {
437            switch (type) {
438            case NOTE:
439                return Diagnostic.Kind.NOTE;
440            case WARNING:
441                return mandatory ? Diagnostic.Kind.MANDATORY_WARNING
442                                 : Diagnostic.Kind.WARNING;
443            case ERROR:
444                return Diagnostic.Kind.ERROR;
445            default:
446                return Diagnostic.Kind.OTHER;
447            }
448        }
449    
450        public String getCode() {
451            return key;
452        }
453    
454        public String getMessage(Locale locale) {
455            return defaultFormatter.formatMessage(this, locale);
456        }
457    
458        public static class MultilineDiagnostic extends JCDiagnostic {
459    
460            private final List<JCDiagnostic> subdiagnostics;
461    
462            public MultilineDiagnostic(JCDiagnostic other, List<JCDiagnostic> subdiagnostics) {
463                super(other.defaultFormatter,
464                      other.getType(),
465                      other.isMandatory(),
466                      other.getDiagnosticSource(),
467                      other.position,
468                      other.getCode(),
469                      other.getArgs());
470                this.subdiagnostics = subdiagnostics;
471            }
472    
473            @Override
474            public List<JCDiagnostic> getSubdiagnostics() {
475                return subdiagnostics;
476            }
477    
478            @Override
479            public boolean isMultiline() {
480                return true;
481            }
482        }
483    }