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.util.*;
045: import java.io.IOException;
046:
047: //TODO - add support for other tags, e.g. server-specific -> make sure they do not block this code
048:
049: /**
050: *
051: * @author mg116726
052: */
053: public class SmapResolver {
054:
055: /** header of SMAP file - must be on the first line
056: */
057: private static final String SMAP_HEADER = "SMAP"; // NOI18N
058:
059: /** default stratum should be set to JSP
060: */
061: private static final String DEFAULT_STRATUM = "JSP"; //NOI18N
062:
063: /** this is how JSP stratum section beginning look like
064: */
065: private static final String STRATUM_SECTION = "*S JSP"; // NOI18N
066:
067: /** line section begins with this mark
068: */
069: private static final String LINE_SECTION = "*L"; // NOI18N
070:
071: /** file section begins with this mark
072: */
073: private static final String FILE_SECTION = "*F"; // NOI18N
074:
075: /** smap file ends with this mark
076: */
077: private static final String END_SECTION = "*E"; // NOI18N
078:
079: /** this hash sign divides fileid from line number
080: */
081: private static final String FID_DELIM = "#"; // NOI18N
082:
083: /** reader that was used to create this smapresolver
084: */
085: private SmapReader reader = null;
086:
087: /** this one is true only if the smap read by the reader has been successfully resolved
088: */
089: private boolean resolved = false;
090:
091: /** default stratum set in the smap file
092: */
093: private String defaultStratum = null;
094:
095: /** outputFileName ==> servlet name set in the smap file
096: */
097: private String outputFileName = null;
098:
099: /** contains hashmap of fileid's & filenames in the jsp
100: */
101: private Hashtable fsection = new Hashtable(3);
102:
103: /** contains jsp -> servlet line mappings
104: */
105: private Map jsp2java = new TreeMap();
106:
107: /** contains servlet -> jsp line mappings
108: */
109: private Map java2jsp = new TreeMap();
110:
111: /** Creates a new instance of SmapResolver
112: * @param reader reader provides readSmap() method which returns SMAP iformation as a String
113: */
114: public SmapResolver(SmapReader reader) {
115: this .resolved = resolve(reader.readSmap());
116: this .reader = reader;
117: }
118:
119: public String toString() {
120: return reader.toString();
121: }
122:
123: /** Reads the smap file and stores all the data into corresponding variables and maps.
124: * At the end calls sanitycheck to check whether the file has been resolved successfuly
125: * @return true if resolved successfuly, false if not
126: * @param smap SMAP information as a string
127: */
128: private boolean resolve(String smap) {
129:
130: String currentSection = "";
131: if (smap == null)
132: return false;
133:
134: // tokenize the smap file by endlines
135: StringTokenizer st = new StringTokenizer(smap, "\n", false);
136:
137: boolean beginning = true;
138: int sectionCounter = 0; // counts items in the sections
139:
140: /** to which file current indexes belong (there are more of them - includes)
141: */
142: String fileIndex = null;
143:
144: while (st.hasMoreTokens()) {
145: String token = st.nextToken();
146:
147: //this tough IF..ELSE is responsible for tracking which section is currently read
148: if (beginning) { // SMAP file begins with 'SMAP' header
149: if (!SMAP_HEADER.equals(token)) {
150: return false;
151: }
152: beginning = false;
153: currentSection = SMAP_HEADER;
154: continue;
155: } else if (STRATUM_SECTION.equals(token)) {
156: currentSection = STRATUM_SECTION;
157: continue;
158: } else if (FILE_SECTION.equals(token)) {
159: currentSection = FILE_SECTION;
160: sectionCounter = 0;
161: continue;
162: } else if (LINE_SECTION.equals(token)) {
163: currentSection = LINE_SECTION;
164: sectionCounter = 0;
165: fileIndex = "0";
166: continue;
167: } else if (END_SECTION.equals(token)) {
168: currentSection = END_SECTION;
169: break;
170: }
171:
172: //read info from header
173: if (SMAP_HEADER.equals(currentSection)) {
174: if (sectionCounter == 0) { // outputFileName
175: outputFileName = token;
176: }
177: if (sectionCounter == 1) { // defaultStratum follows
178: defaultStratum = token;
179: }
180: }
181:
182: //read the file section
183: if (FILE_SECTION.equals(currentSection)) {
184: if (token.startsWith("+")) {
185: sectionCounter++;
186: storeFile(token, token = st.nextToken());
187: } else {
188: storeFile(token, null);
189: }
190: }
191:
192: if (LINE_SECTION.equals(currentSection)) {
193: int hashPresent = token.indexOf(FID_DELIM);
194: if (hashPresent > -1) { // there's a hash => there's a fileid indicator
195: fileIndex = token.substring(hashPresent + 1, token
196: .indexOf(':'));
197: if ((fileIndex != null)
198: && (fileIndex.indexOf(',') > -1)) {
199: fileIndex = fileIndex.substring(0, fileIndex
200: .indexOf(','));
201: }
202:
203: }
204: storeLine(token, fileIndex);
205: }
206: sectionCounter++;
207: }
208:
209: //perform sanity check - report error (return false) if unsuccessful
210: this .resolved = sanityCheck();
211: return this .resolved;
212: }
213:
214: /** stores file name and file index into the fsection map
215: */
216: private void storeFile(String token, String token2) {
217: String id = "";
218: String filename = "";
219: int spaceIndex = 0;
220: if ((token != null) && (token.startsWith("+"))) {
221: token = token.substring(2);
222: spaceIndex = token.indexOf(" ");
223: id = token.substring(0, spaceIndex);
224: filename = token2;
225: } else {
226: spaceIndex = token.indexOf(" ");
227: id = token.substring(0, spaceIndex);
228: filename = token.substring(spaceIndex + 1);
229: }
230: fsection.put(id, filename);
231: }
232:
233: /** stores line mappings into both java->jsp->java maps
234: */
235: private void storeLine(String token, String fileIndex) {
236: // System.err.println("storeLine: " + token + ", " + fileIndex);
237: int delimIndex = token.indexOf(":");
238:
239: String jspLine = token.substring(0, delimIndex);
240: String javaLine = token.substring(delimIndex + 1);
241:
242: int hashPresent = jspLine.indexOf(FID_DELIM);
243: int commaPresent = jspLine.indexOf(',');
244:
245: int jspIndex = 0;
246: int repeatCount = 0;
247:
248: if (commaPresent != -1) {
249: repeatCount = Integer.parseInt(jspLine
250: .substring(commaPresent + 1));
251: if (hashPresent == -1) {
252: jspIndex = Integer.parseInt(jspLine.substring(0,
253: commaPresent));
254: } else {
255: jspIndex = Integer.parseInt(jspLine.substring(0,
256: hashPresent));
257: }
258: } else {
259: if (hashPresent == -1) {
260: jspIndex = Integer.parseInt(jspLine);
261: } else {
262: jspIndex = Integer.parseInt(jspLine.substring(0,
263: hashPresent));
264: }
265: repeatCount = 1;
266: }
267:
268: commaPresent = javaLine.indexOf(',');
269:
270: int outputIncrement;
271: int javaIndex;
272: if (commaPresent != -1) {
273: outputIncrement = Integer.parseInt(javaLine
274: .substring(commaPresent + 1));
275: javaIndex = Integer.parseInt(javaLine.substring(0,
276: commaPresent));
277: } else {
278: outputIncrement = 1;
279: javaIndex = Integer.parseInt(javaLine);
280: }
281:
282: for (int i = 0; i < repeatCount; i++) {
283: int jspL = jspIndex + i;
284: int javaL = javaIndex + (i * outputIncrement);
285:
286: // fill in table for jsp->java mappings
287: jspLine = Integer.toString(jspL).concat(FID_DELIM).concat(
288: fileIndex);
289: javaLine = Integer.toString(javaL);
290: if (!jsp2java.containsKey(jspLine)) { // the first rule is the right one
291: jsp2java.put(jspLine, javaLine);
292: }
293:
294: // fill in table for java->jsp mappings
295: jspLine = Integer.toString(jspL).concat("#").concat(
296: fileIndex);
297: javaLine = Integer.toString(javaL);
298: if (!java2jsp.containsKey(javaLine)) { // the first rule is the right one
299: java2jsp.put(javaLine, jspLine);
300: }
301: }
302: }
303:
304: /** check whether the file has been resolved correctly
305: */
306: private boolean sanityCheck() {
307: if (!DEFAULT_STRATUM.equals(defaultStratum))
308: return false;
309: if (!(outputFileName.endsWith(".java")))
310: return false;
311: if (fsection.isEmpty())
312: return false;
313: if (jsp2java.isEmpty())
314: return false; // TODO: check how this is done for empty jsps
315: if (java2jsp.isEmpty())
316: return false; // TODO: check how this is done for empty jsps
317: return true;
318: }
319:
320: /** access file name by index in the SMAP
321: * @param index Index of the file in the SMAP
322: * @return filename
323: */
324: private String getFileNameByIndex(String index) {
325: return (String) fsection.get(index);
326: }
327:
328: /** access index by the filename
329: * @param fname filename to find index for
330: * @return index of the file in SMAP
331: */
332: private String getIndexByFileName(String fname) {
333: Set s = fsection.entrySet();
334: Iterator i = s.iterator();
335: while (i.hasNext()) {
336: Map.Entry mentry = (Map.Entry) i.next();
337: String value = (String) mentry.getValue();
338: if (value.equalsIgnoreCase(fname)) {
339: return mentry.getKey().toString();
340: }
341: }
342: return null;
343: }
344:
345: /**
346: * @return Whether the SMAP info file is resolved or not
347: */
348: public boolean isResolved() {
349: return this .resolved;
350: }
351:
352: /**
353: * get all the filenames in the SMAP
354: */
355: public Map getFileNames() {
356: Hashtable h = new Hashtable(fsection.size());
357: Collection c = fsection.values();
358: Iterator i = c.iterator();
359: int counter = 0;
360: while (i.hasNext()) {
361: h.put(new Integer(counter++), i.next());
362: }
363: return h;
364: }
365:
366: /**
367: * get primary jsp filename
368: */
369: public String getPrimaryJspFileName() {
370: TreeMap tm = new TreeMap(fsection);
371: Object o = tm.firstKey();
372: String s = (String) fsection.get(o);
373: return s;
374: }
375:
376: /** if there are included files in the jsp or not
377: */
378: public boolean hasIncludedFiles() {
379: return (fsection.size() > 1);
380: }
381:
382: public String getJavaLineType(int line, int col) {
383: //line type is not included in the SMAP mapping information - therefore this is not supported in JSR45
384: return null;
385: }
386:
387: /** whether the jsp file is completely empty - without any line
388: */
389: public boolean isEmpty() {
390: return jsp2java.isEmpty(); // TODO - check if this really works
391: }
392:
393: /** returns jsp name for corresponding servlet line
394: */
395: public String getJspFileName(int line, int col) throws IOException {
396: String key = Integer.toString(line);
397: String value = (String) java2jsp.get(key);
398: if (value == null)
399: return null;
400: String index = value.substring(value.indexOf(FID_DELIM) + 1);
401: return getFileNameByIndex(index);
402: }
403:
404: public int mangle(String jspFileName, int line, int col) {
405: String fileIndex = getIndexByFileName(jspFileName);
406: if (fileIndex == null)
407: return -1;
408: String key = "".concat(Integer.toString(line)).concat("#")
409: .concat(fileIndex);
410: String value = (String) jsp2java.get(key);
411: if (value == null)
412: return -1;
413: return Integer.parseInt(value);
414: }
415:
416: public int unmangle(int line, int col) {
417: String key = Integer.toString(line);
418: String value = (String) java2jsp.get(key);
419: if (value == null)
420: return -1;
421: int jspline = Integer.parseInt(value.substring(0, value
422: .indexOf("#")));
423: return jspline;
424: }
425:
426: // public static void main(String[] args) {
427: // SmapResolver sr = new SmapResolver(new SmapFileReader(
428: // new java.io.File("/Users/mg116726/WebApplication3/build/generated/src/org/apache/jsp/index_jsp.class.smap")));
429: // System.err.println("resolved: " + sr.isResolved());
430: // try {
431: // System.err.println(sr.getJspFileName(43,0));
432: // System.err.println(sr.unmangle(43,0));
433: // } catch (Exception e) {
434: // System.err.println("exception");
435: // }
436: // }
437: }
|