001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.web.jspcompiler;
043:
044: import java.io.File;
045: import java.util.logging.Level;
046: import java.util.logging.Logger;
047: import java.util.regex.Matcher;
048: import java.util.regex.Pattern;
049: import org.apache.tools.ant.module.spi.AntEvent;
050: import org.apache.tools.ant.module.spi.AntLogger;
051: import org.apache.tools.ant.module.spi.AntSession;
052: import org.netbeans.modules.web.api.webmodule.WebModule;
053: import org.openide.filesystems.FileObject;
054: import org.openide.filesystems.FileStateInvalidException;
055: import org.openide.filesystems.FileUtil;
056:
057: /**
058: * Ant logger which handles compilation of JSPs, both the JSP -> Java and
059: * the Java -> class compilation stages.
060: * Specifically, handles hyperlinking of errors from JspC and from Javac run on
061: * classes generated from JSPs.
062: * @author Petr Jiricka, Jesse Glick
063: * @see "#42525"
064: */
065: public final class JSPAntLogger extends AntLogger {
066:
067: /**
068: * Regexp matching the compilation error from JspC. Sample message could look like this:
069: * org.apache.jasper.JasperException: file:C:/project/AntParseTestProject2/build/web/index.jsp(6,0) Include action: Mandatory attribute page missing
070: */
071: private static final Pattern JSP_COMPILER_ERROR = Pattern
072: .compile("(.*)(org.apache.jasper.JasperException: file:)(.*)"); // NOI18N
073:
074: private static final Pattern FILE_PATTERN = Pattern
075: .compile("([^\\(]*)\\(([0-9]+),([0-9]+)\\)"); // NOI18N
076:
077: private static final String[] TASKS_OF_INTEREST = AntLogger.ALL_TASKS;
078:
079: private static final int[] LEVELS_OF_INTEREST = {
080: //AntEvent.LOG_DEBUG, // XXX is this needed?
081: //AntEvent.LOG_VERBOSE, // XXX is this needed?
082: AntEvent.LOG_INFO, // XXX is this needed?
083: AntEvent.LOG_WARN, // XXX is this needed?
084: AntEvent.LOG_ERR, // XXX is this needed?
085: };
086:
087: private static final Logger ERR = Logger
088: .getLogger(JSPAntLogger.class.getName());
089: private static final boolean LOGGABLE = ERR.isLoggable(Level.FINE);
090:
091: /** Default constructor for lookup. */
092: public JSPAntLogger() {
093: }
094:
095: public boolean interestedInSession(AntSession session) {
096: return true;
097: }
098:
099: public boolean interestedInAllScripts(AntSession session) {
100: return true;
101: }
102:
103: public String[] interestedInTargets(AntSession session) {
104: return AntLogger.ALL_TARGETS;
105: }
106:
107: public boolean interestedInScript(File script, AntSession session) {
108: return true;
109: }
110:
111: public String[] interestedInTasks(AntSession session) {
112: return TASKS_OF_INTEREST;
113: }
114:
115: public int[] interestedInLogLevels(AntSession session) {
116: // XXX could exclude those in [INFO..ERR] greater than session.verbosity
117: return LEVELS_OF_INTEREST;
118: }
119:
120: public void messageLogged(AntEvent event) {
121: AntSession session = event.getSession();
122: int messageLevel = event.getLogLevel();
123: int sessionLevel = session.getVerbosity();
124: String line = event.getMessage();
125: assert line != null;
126:
127: // XXX only check when the task is correct
128: Matcher m = JSP_COMPILER_ERROR.matcher(line);
129: if (m.matches()) { //it's our error
130: if (LOGGABLE)
131: ERR.log(Level.FINE, "matched line: " + line);
132: // print the exception and error statement first
133: String jspErrorText = line
134: .substring(line.lastIndexOf(')') + 1);
135: session.println(line.substring(0, line.indexOf("file:"))
136: + jspErrorText, true, null);
137:
138: // get the files from the line
139: String filePart = line.substring(line.indexOf("file"), line
140: .lastIndexOf(')') + 1);
141: if (LOGGABLE)
142: ERR.log(Level.FINE, "file part: " + filePart);
143:
144: // now create hyperlinks for all the files
145: int startIndex = 0;
146: while (filePart.indexOf("file:", startIndex) > -1) {
147: int start = filePart.indexOf("file:", startIndex) + 5;
148: int end = filePart.indexOf(')', startIndex) + 1;
149: startIndex = end;
150: String file = filePart.substring(start, end);
151: if (LOGGABLE)
152: ERR.log(Level.FINE, "file: " + file);
153:
154: // we've got the info for one file extracted, now extract the line/column and actual filename
155: Matcher fileMatcher = FILE_PATTERN.matcher(file);
156: if (fileMatcher.matches()) {
157: String jspFile = fileMatcher.group(1).trim();
158: int lineNumber = Integer.parseInt(fileMatcher
159: .group(2));
160: int columnNumber = Integer.parseInt(fileMatcher
161: .group(3)) + 1;
162: if (LOGGABLE)
163: ERR.log(Level.FINE, "linking line: "
164: + lineNumber + ", column: "
165: + columnNumber);
166:
167: File f = new File(jspFile);
168: FileObject fo = FileUtil.toFileObject(f);
169: // Check to see if this JSP is in the web module.
170: FileObject jspSource = getResourceInSources(fo);
171: // and create the hyperlink if needed
172: if (jspSource != null) {
173: if (messageLevel <= sessionLevel
174: && !event.isConsumed()) {
175: try {
176: session.println(file, true, session
177: .createStandardHyperlink(
178: jspSource.getURL(),
179: jspErrorText,
180: lineNumber,
181: columnNumber, -1, -1));
182: } catch (FileStateInvalidException e) {
183: assert false : e;
184: }
185: }
186: }
187: }
188: }
189: event.consume();
190: }
191: }
192:
193: /** Given a resource in the build directory, returns the corresponding resource in the source directory.
194: */
195: private static FileObject getResourceInSources(FileObject inBuild) {
196: if (inBuild == null) {
197: return null;
198: }
199: WebModule wm = WebModule.getWebModule(inBuild);
200: if (wm != null) {
201: // get the mirror of this page in sources
202: FileObject webBuildDir = guessWebModuleOutputRoot(wm,
203: inBuild);
204: if (webBuildDir != null) {
205: String jspResourcePath = FileUtil.getRelativePath(
206: webBuildDir, inBuild);
207: return wm.getDocumentBase().getFileObject(
208: jspResourcePath);
209: }
210:
211: }
212: return null;
213: }
214:
215: private static FileObject guessWebModuleOutputRoot(WebModule wm,
216: FileObject fo) {
217: /*
218: File outputF = wm.getWebOutputRoot();
219: if (outputF != null) {
220: return FileUtil.toFileObject(outputF);
221: }
222: */
223: FileObject potentialRoot = fo.getParent();
224: while (potentialRoot != null) {
225: if (potentialRoot.getFileObject("WEB-INF") != null) {
226: return potentialRoot;
227: }
228: potentialRoot = potentialRoot.getParent();
229: }
230: return null;
231: }
232:
233: }
|