001    /*
002     * Copyright 1999-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.io.IOException;
029    import java.lang.ref.SoftReference;
030    import java.nio.CharBuffer;
031    import java.util.Map;
032    import javax.tools.JavaFileObject;
033    
034    import com.sun.tools.javac.file.JavacFileManager;
035    import com.sun.tools.javac.tree.JCTree;
036    
037    import static com.sun.tools.javac.util.LayoutCharacters.*;
038    
039    /**
040     * A simple abstraction of a source file, as needed for use in a diagnostic message.
041     * Provides access to the line and position in a line for any given character offset.
042     *
043     *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
044     *  you write code that depends on this, you do so at your own risk.
045     *  This code and its internal interfaces are subject to change or
046     *  deletion without notice.</b>
047     */
048    public class DiagnosticSource {
049        public DiagnosticSource(JavaFileObject fo, AbstractLog log) {
050            this.fileObject = fo;
051            this.log = log;
052        }
053    
054        /** Return the underlying file object handled by this
055         *  DiagnosticSource object.
056         */
057        public JavaFileObject getFile() {
058            return fileObject;
059        }
060    
061        public CharSequence getName()  {
062            return JavacFileManager.getJavacBaseFileName(fileObject);
063        }
064    
065        /** Return the one-based line number associated with a given pos
066         * for the current source file.  Zero is returned if no line exists
067         * for the given position.
068         */
069        public int getLineNumber(int pos) {
070            try {
071                if (findLine(pos)) {
072                    return line;
073                }
074                return 0;
075            } finally {
076                buf = null;
077            }
078        }
079    
080        /** Return the one-based column number associated with a given pos
081         * for the current source file.  Zero is returned if no column exists
082         * for the given position.
083         */
084        public int getColumnNumber(int pos, boolean expandTabs) {
085            try {
086                if (findLine(pos)) {
087                    int column = 0;
088                    for (int bp = lineStart; bp < pos; bp++) {
089                        if (bp >= bufLen) {
090                            return 0;
091                        }
092                        if (buf[bp] == '\t' && expandTabs) {
093                            column = (column / TabInc * TabInc) + TabInc;
094                        } else {
095                            column++;
096                        }
097                    }
098                    return column + 1; // positions are one-based
099                }
100                return 0;
101            } finally {
102                buf = null;
103            }
104        }
105    
106        /** Return the content of the line containing a given pos.
107         */
108        public String getLine(int pos) {
109            try {
110                if (!findLine(pos))
111                    return null;
112    
113                int lineEnd = lineStart;
114                while (lineEnd < bufLen && buf[lineEnd] != CR && buf[lineEnd] != LF)
115                    lineEnd++;
116                if (lineEnd - lineStart == 0)
117                    return null;
118                return new String(buf, lineStart, lineEnd - lineStart);
119            } finally {
120                buf = null;
121            }
122        }
123    
124        public Map<JCTree, Integer> getEndPosTable() {
125            return endPosTable;
126        }
127    
128        public void setEndPosTable(Map<JCTree, Integer> t) {
129            if (endPosTable != null && endPosTable != t)
130                throw new IllegalStateException("endPosTable already set");
131            endPosTable = t;
132        }
133    
134        /** Find the line in the buffer that contains the current position
135         * @param pos      Character offset into the buffer
136         */
137        private boolean findLine(int pos) {
138            if (pos == Position.NOPOS)
139                return false;
140    
141            try {
142                // try and recover buffer from soft reference cache
143                if (buf == null && refBuf != null)
144                    buf = refBuf.get();
145    
146                if (buf == null) {
147                    buf = initBuf(fileObject);
148                    lineStart = 0;
149                    line = 1;
150                } else if (lineStart > pos) { // messages don't come in order
151                    lineStart = 0;
152                    line = 1;
153                }
154    
155                int bp = lineStart;
156                while (bp < bufLen && bp < pos) {
157                    switch (buf[bp++]) {
158                    case CR:
159                        if (bp < bufLen && buf[bp] == LF) bp++;
160                        line++;
161                        lineStart = bp;
162                        break;
163                    case LF:
164                        line++;
165                        lineStart = bp;
166                        break;
167                    }
168                }
169                return bp <= bufLen;
170            } catch (IOException e) {
171                log.directError("source.unavailable");
172                buf = new char[0];
173                return false;
174            }
175        }
176    
177        protected char[] initBuf(JavaFileObject fileObject) throws IOException {
178            char[] buf;
179            CharSequence cs = fileObject.getCharContent(true);
180            if (cs instanceof CharBuffer) {
181                CharBuffer cb = (CharBuffer) cs;
182                buf = JavacFileManager.toArray(cb);
183                bufLen = cb.limit();
184            } else {
185                buf = cs.toString().toCharArray();
186                bufLen = buf.length;
187            }
188            refBuf = new SoftReference<char[]>(buf);
189            return buf;
190        }
191    
192        /** The underlying file object. */
193        protected JavaFileObject fileObject;
194    
195        protected Map<JCTree, Integer> endPosTable;
196    
197        /** A soft reference to the content of the file object. */
198        protected SoftReference<char[]> refBuf;
199    
200        /** A temporary hard reference to the content of the file object. */
201        protected char[] buf;
202    
203        /** The length of the content. */
204        protected int bufLen;
205    
206        /** The start of a line found by findLine. */
207        protected int lineStart;
208    
209        /** The line number of a line found by findLine. */
210        protected int line;
211    
212        /** A log for reporting errors, such as errors accessing the content. */
213        protected AbstractLog log;
214    }