KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jasper > compiler > SmapUtil


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.jasper.compiler;
19
20 import java.io.File JavaDoc;
21 import java.io.FileInputStream JavaDoc;
22 import java.io.FileNotFoundException JavaDoc;
23 import java.io.FileOutputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.OutputStreamWriter JavaDoc;
26 import java.io.PrintWriter JavaDoc;
27 import java.io.UnsupportedEncodingException JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.Map JavaDoc;
31
32 import org.apache.jasper.JasperException;
33 import org.apache.jasper.JspCompilationContext;
34
35 /**
36  * Contains static utilities for generating SMAP data based on the
37  * current version of Jasper.
38  *
39  * @author Jayson Falkner
40  * @author Shawn Bayern
41  * @author Robert Field (inner SDEInstaller class)
42  * @author Mark Roth
43  * @author Kin-man Chung
44  */

45 public class SmapUtil {
46
47     private org.apache.commons.logging.Log log=
48         org.apache.commons.logging.LogFactory.getLog( SmapUtil.class );
49
50     //*********************************************************************
51
// Constants
52

53     public static final String JavaDoc SMAP_ENCODING = "UTF-8";
54
55     //*********************************************************************
56
// Public entry points
57

58     /**
59      * Generates an appropriate SMAP representing the current compilation
60      * context. (JSR-045.)
61      *
62      * @param ctxt Current compilation context
63      * @param pageNodes The current JSP page
64      * @return a SMAP for the page
65      */

66     public static String JavaDoc[] generateSmap(
67         JspCompilationContext ctxt,
68         Node.Nodes pageNodes)
69         throws IOException JavaDoc {
70
71         // Scan the nodes for presence of Jasper generated inner classes
72
PreScanVisitor psVisitor = new PreScanVisitor();
73         try {
74             pageNodes.visit(psVisitor);
75         } catch (JasperException ex) {
76         }
77         HashMap JavaDoc map = psVisitor.getMap();
78
79         // set up our SMAP generator
80
SmapGenerator g = new SmapGenerator();
81         
82         /** Disable reading of input SMAP because:
83             1. There is a bug here: getRealPath() is null if .jsp is in a jar
84             Bugzilla 14660.
85             2. Mappings from other sources into .jsp files are not supported.
86             TODO: fix 1. if 2. is not true.
87         // determine if we have an input SMAP
88         String smapPath = inputSmapPath(ctxt.getRealPath(ctxt.getJspFile()));
89             File inputSmap = new File(smapPath);
90             if (inputSmap.exists()) {
91                 byte[] embeddedSmap = null;
92             byte[] subSmap = SDEInstaller.readWhole(inputSmap);
93             String subSmapString = new String(subSmap, SMAP_ENCODING);
94             g.addSmap(subSmapString, "JSP");
95         }
96         **/

97
98         // now, assemble info about our own stratum (JSP) using JspLineMap
99
SmapStratum s = new SmapStratum("JSP");
100
101         g.setOutputFileName(unqualify(ctxt.getServletJavaFileName()));
102
103         // Map out Node.Nodes
104
evaluateNodes(pageNodes, s, map, ctxt.getOptions().getMappedFile());
105         s.optimizeLineSection();
106         g.addStratum(s, true);
107
108         if (ctxt.getOptions().isSmapDumped()) {
109             File JavaDoc outSmap = new File JavaDoc(ctxt.getClassFileName() + ".smap");
110             PrintWriter JavaDoc so =
111                 new PrintWriter JavaDoc(
112                     new OutputStreamWriter JavaDoc(
113                         new FileOutputStream JavaDoc(outSmap),
114                         SMAP_ENCODING));
115             so.print(g.getString());
116             so.close();
117         }
118
119         String JavaDoc classFileName = ctxt.getClassFileName();
120         int innerClassCount = map.size();
121         String JavaDoc [] smapInfo = new String JavaDoc[2 + innerClassCount*2];
122         smapInfo[0] = classFileName;
123         smapInfo[1] = g.getString();
124
125         int count = 2;
126         Iterator JavaDoc iter = map.entrySet().iterator();
127         while (iter.hasNext()) {
128             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
129             String JavaDoc innerClass = (String JavaDoc) entry.getKey();
130             s = (SmapStratum) entry.getValue();
131             s.optimizeLineSection();
132             g = new SmapGenerator();
133             g.setOutputFileName(unqualify(ctxt.getServletJavaFileName()));
134             g.addStratum(s, true);
135
136             String JavaDoc innerClassFileName =
137                 classFileName.substring(0, classFileName.indexOf(".class")) +
138                 '$' + innerClass + ".class";
139             if (ctxt.getOptions().isSmapDumped()) {
140                 File JavaDoc outSmap = new File JavaDoc(innerClassFileName + ".smap");
141                 PrintWriter JavaDoc so =
142                     new PrintWriter JavaDoc(
143                         new OutputStreamWriter JavaDoc(
144                             new FileOutputStream JavaDoc(outSmap),
145                             SMAP_ENCODING));
146                 so.print(g.getString());
147                 so.close();
148             }
149             smapInfo[count] = innerClassFileName;
150             smapInfo[count+1] = g.getString();
151             count += 2;
152         }
153
154         return smapInfo;
155     }
156
157     public static void installSmap(String JavaDoc[] smap)
158         throws IOException JavaDoc {
159         if (smap == null) {
160             return;
161         }
162
163         for (int i = 0; i < smap.length; i += 2) {
164             File JavaDoc outServlet = new File JavaDoc(smap[i]);
165             SDEInstaller.install(outServlet, smap[i+1].getBytes());
166         }
167     }
168
169     //*********************************************************************
170
// Private utilities
171

172     /**
173      * Returns an unqualified version of the given file path.
174      */

175     private static String JavaDoc unqualify(String JavaDoc path) {
176         path = path.replace('\\', '/');
177         return path.substring(path.lastIndexOf('/') + 1);
178     }
179
180     /**
181      * Returns a file path corresponding to a potential SMAP input
182      * for the given compilation input (JSP file).
183      */

184     private static String JavaDoc inputSmapPath(String JavaDoc path) {
185         return path.substring(0, path.lastIndexOf('.') + 1) + "smap";
186     }
187
188     //*********************************************************************
189
// Installation logic (from Robert Field, JSR-045 spec lead)
190
private static class SDEInstaller {
191
192         private org.apache.commons.logging.Log log=
193             org.apache.commons.logging.LogFactory.getLog( SDEInstaller.class );
194
195         static final String JavaDoc nameSDE = "SourceDebugExtension";
196
197         byte[] orig;
198         byte[] sdeAttr;
199         byte[] gen;
200
201         int origPos = 0;
202         int genPos = 0;
203
204         int sdeIndex;
205
206         public static void main(String JavaDoc[] args) throws IOException JavaDoc {
207             if (args.length == 2) {
208                 install(new File JavaDoc(args[0]), new File JavaDoc(args[1]));
209             } else if (args.length == 3) {
210                 install(
211                     new File JavaDoc(args[0]),
212                     new File JavaDoc(args[1]),
213                     new File JavaDoc(args[2]));
214             } else {
215                 System.err.println(
216                     "Usage: <command> <input class file> "
217                         + "<attribute file> <output class file name>\n"
218                         + "<command> <input/output class file> <attribute file>");
219             }
220         }
221
222         static void install(File JavaDoc inClassFile, File JavaDoc attrFile, File JavaDoc outClassFile)
223             throws IOException JavaDoc {
224             new SDEInstaller(inClassFile, attrFile, outClassFile);
225         }
226
227         static void install(File JavaDoc inOutClassFile, File JavaDoc attrFile)
228             throws IOException JavaDoc {
229             File JavaDoc tmpFile = new File JavaDoc(inOutClassFile.getPath() + "tmp");
230             new SDEInstaller(inOutClassFile, attrFile, tmpFile);
231             if (!inOutClassFile.delete()) {
232                 throw new IOException JavaDoc("inOutClassFile.delete() failed");
233             }
234             if (!tmpFile.renameTo(inOutClassFile)) {
235                 throw new IOException JavaDoc("tmpFile.renameTo(inOutClassFile) failed");
236             }
237         }
238
239         static void install(File JavaDoc classFile, byte[] smap) throws IOException JavaDoc {
240             File JavaDoc tmpFile = new File JavaDoc(classFile.getPath() + "tmp");
241             new SDEInstaller(classFile, smap, tmpFile);
242             if (!classFile.delete()) {
243                 throw new IOException JavaDoc("classFile.delete() failed");
244             }
245             if (!tmpFile.renameTo(classFile)) {
246                 throw new IOException JavaDoc("tmpFile.renameTo(classFile) failed");
247             }
248         }
249
250         SDEInstaller(File JavaDoc inClassFile, byte[] sdeAttr, File JavaDoc outClassFile)
251             throws IOException JavaDoc {
252             if (!inClassFile.exists()) {
253                 throw new FileNotFoundException JavaDoc("no such file: " + inClassFile);
254             }
255
256             this.sdeAttr = sdeAttr;
257             // get the bytes
258
orig = readWhole(inClassFile);
259             gen = new byte[orig.length + sdeAttr.length + 100];
260
261             // do it
262
addSDE();
263
264             // write result
265
FileOutputStream JavaDoc outStream = new FileOutputStream JavaDoc(outClassFile);
266             outStream.write(gen, 0, genPos);
267             outStream.close();
268         }
269
270         SDEInstaller(File JavaDoc inClassFile, File JavaDoc attrFile, File JavaDoc outClassFile)
271             throws IOException JavaDoc {
272             this(inClassFile, readWhole(attrFile), outClassFile);
273         }
274
275         static byte[] readWhole(File JavaDoc input) throws IOException JavaDoc {
276             FileInputStream JavaDoc inStream = new FileInputStream JavaDoc(input);
277             int len = (int)input.length();
278             byte[] bytes = new byte[len];
279             if (inStream.read(bytes, 0, len) != len) {
280                 throw new IOException JavaDoc("expected size: " + len);
281             }
282             inStream.close();
283             return bytes;
284         }
285
286         void addSDE() throws UnsupportedEncodingException JavaDoc, IOException JavaDoc {
287             int i;
288             copy(4 + 2 + 2); // magic min/maj version
289
int constantPoolCountPos = genPos;
290             int constantPoolCount = readU2();
291             if (log.isDebugEnabled())
292                 log.debug("constant pool count: " + constantPoolCount);
293             writeU2(constantPoolCount);
294
295             // copy old constant pool return index of SDE symbol, if found
296
sdeIndex = copyConstantPool(constantPoolCount);
297             if (sdeIndex < 0) {
298                 // if "SourceDebugExtension" symbol not there add it
299
writeUtf8ForSDE();
300
301                 // increment the countantPoolCount
302
sdeIndex = constantPoolCount;
303                 ++constantPoolCount;
304                 randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
305
306                 if (log.isDebugEnabled())
307                     log.debug("SourceDebugExtension not found, installed at: " + sdeIndex);
308             } else {
309                 if (log.isDebugEnabled())
310                     log.debug("SourceDebugExtension found at: " + sdeIndex);
311             }
312             copy(2 + 2 + 2); // access, this, super
313
int interfaceCount = readU2();
314             writeU2(interfaceCount);
315             if (log.isDebugEnabled())
316                 log.debug("interfaceCount: " + interfaceCount);
317             copy(interfaceCount * 2);
318             copyMembers(); // fields
319
copyMembers(); // methods
320
int attrCountPos = genPos;
321             int attrCount = readU2();
322             writeU2(attrCount);
323             if (log.isDebugEnabled())
324                 log.debug("class attrCount: " + attrCount);
325             // copy the class attributes, return true if SDE attr found (not copied)
326
if (!copyAttrs(attrCount)) {
327                 // we will be adding SDE and it isn't already counted
328
++attrCount;
329                 randomAccessWriteU2(attrCountPos, attrCount);
330                 if (log.isDebugEnabled())
331                     log.debug("class attrCount incremented");
332             }
333             writeAttrForSDE(sdeIndex);
334         }
335
336         void copyMembers() {
337             int count = readU2();
338             writeU2(count);
339             if (log.isDebugEnabled())
340                 log.debug("members count: " + count);
341             for (int i = 0; i < count; ++i) {
342                 copy(6); // access, name, descriptor
343
int attrCount = readU2();
344                 writeU2(attrCount);
345                 if (log.isDebugEnabled())
346                     log.debug("member attr count: " + attrCount);
347                 copyAttrs(attrCount);
348             }
349         }
350
351         boolean copyAttrs(int attrCount) {
352             boolean sdeFound = false;
353             for (int i = 0; i < attrCount; ++i) {
354                 int nameIndex = readU2();
355                 // don't write old SDE
356
if (nameIndex == sdeIndex) {
357                     sdeFound = true;
358                     if (log.isDebugEnabled())
359                         log.debug("SDE attr found");
360                 } else {
361                     writeU2(nameIndex); // name
362
int len = readU4();
363                     writeU4(len);
364                     copy(len);
365                     if (log.isDebugEnabled())
366                         log.debug("attr len: " + len);
367                 }
368             }
369             return sdeFound;
370         }
371
372         void writeAttrForSDE(int index) {
373             writeU2(index);
374             writeU4(sdeAttr.length);
375             for (int i = 0; i < sdeAttr.length; ++i) {
376                 writeU1(sdeAttr[i]);
377             }
378         }
379
380         void randomAccessWriteU2(int pos, int val) {
381             int savePos = genPos;
382             genPos = pos;
383             writeU2(val);
384             genPos = savePos;
385         }
386
387         int readU1() {
388             return ((int)orig[origPos++]) & 0xFF;
389         }
390
391         int readU2() {
392             int res = readU1();
393             return (res << 8) + readU1();
394         }
395
396         int readU4() {
397             int res = readU2();
398             return (res << 16) + readU2();
399         }
400
401         void writeU1(int val) {
402             gen[genPos++] = (byte)val;
403         }
404
405         void writeU2(int val) {
406             writeU1(val >> 8);
407             writeU1(val & 0xFF);
408         }
409
410         void writeU4(int val) {
411             writeU2(val >> 16);
412             writeU2(val & 0xFFFF);
413         }
414
415         void copy(int count) {
416             for (int i = 0; i < count; ++i) {
417                 gen[genPos++] = orig[origPos++];
418             }
419         }
420
421         byte[] readBytes(int count) {
422             byte[] bytes = new byte[count];
423             for (int i = 0; i < count; ++i) {
424                 bytes[i] = orig[origPos++];
425             }
426             return bytes;
427         }
428
429         void writeBytes(byte[] bytes) {
430             for (int i = 0; i < bytes.length; ++i) {
431                 gen[genPos++] = bytes[i];
432             }
433         }
434
435         int copyConstantPool(int constantPoolCount)
436             throws UnsupportedEncodingException JavaDoc, IOException JavaDoc {
437             int sdeIndex = -1;
438             // copy const pool index zero not in class file
439
for (int i = 1; i < constantPoolCount; ++i) {
440                 int tag = readU1();
441                 writeU1(tag);
442                 switch (tag) {
443                     case 7 : // Class
444
case 8 : // String
445
if (log.isDebugEnabled())
446                             log.debug(i + " copying 2 bytes");
447                         copy(2);
448                         break;
449                     case 9 : // Field
450
case 10 : // Method
451
case 11 : // InterfaceMethod
452
case 3 : // Integer
453
case 4 : // Float
454
case 12 : // NameAndType
455
if (log.isDebugEnabled())
456                             log.debug(i + " copying 4 bytes");
457                         copy(4);
458                         break;
459                     case 5 : // Long
460
case 6 : // Double
461
if (log.isDebugEnabled())
462                             log.debug(i + " copying 8 bytes");
463                         copy(8);
464                         i++;
465                         break;
466                     case 1 : // Utf8
467
int len = readU2();
468                         writeU2(len);
469                         byte[] utf8 = readBytes(len);
470                         String JavaDoc str = new String JavaDoc(utf8, "UTF-8");
471                         if (log.isDebugEnabled())
472                             log.debug(i + " read class attr -- '" + str + "'");
473                         if (str.equals(nameSDE)) {
474                             sdeIndex = i;
475                         }
476                         writeBytes(utf8);
477                         break;
478                     default :
479                         throw new IOException JavaDoc("unexpected tag: " + tag);
480                 }
481             }
482             return sdeIndex;
483         }
484
485         void writeUtf8ForSDE() {
486             int len = nameSDE.length();
487             writeU1(1); // Utf8 tag
488
writeU2(len);
489             for (int i = 0; i < len; ++i) {
490                 writeU1(nameSDE.charAt(i));
491             }
492         }
493     }
494
495     public static void evaluateNodes(
496         Node.Nodes nodes,
497         SmapStratum s,
498         HashMap JavaDoc innerClassMap,
499         boolean breakAtLF) {
500         try {
501             nodes.visit(new SmapGenVisitor(s, breakAtLF, innerClassMap));
502         } catch (JasperException ex) {
503         }
504     }
505
506     static class SmapGenVisitor extends Node.Visitor {
507
508         private SmapStratum smap;
509         private boolean breakAtLF;
510         private HashMap JavaDoc innerClassMap;
511
512         SmapGenVisitor(SmapStratum s, boolean breakAtLF, HashMap JavaDoc map) {
513             this.smap = s;
514             this.breakAtLF = breakAtLF;
515             this.innerClassMap = map;
516         }
517
518         public void visitBody(Node n) throws JasperException {
519             SmapStratum smapSave = smap;
520             String JavaDoc innerClass = n.getInnerClassName();
521             if (innerClass != null) {
522                 this.smap = (SmapStratum) innerClassMap.get(innerClass);
523             }
524             super.visitBody(n);
525             smap = smapSave;
526         }
527
528         public void visit(Node.Declaration n) throws JasperException {
529             doSmapText(n);
530         }
531
532         public void visit(Node.Expression n) throws JasperException {
533             doSmapText(n);
534         }
535
536         public void visit(Node.Scriptlet n) throws JasperException {
537             doSmapText(n);
538         }
539
540         public void visit(Node.IncludeAction n) throws JasperException {
541             doSmap(n);
542             visitBody(n);
543         }
544
545         public void visit(Node.ForwardAction n) throws JasperException {
546             doSmap(n);
547             visitBody(n);
548         }
549
550         public void visit(Node.GetProperty n) throws JasperException {
551             doSmap(n);
552             visitBody(n);
553         }
554
555         public void visit(Node.SetProperty n) throws JasperException {
556             doSmap(n);
557             visitBody(n);
558         }
559
560         public void visit(Node.UseBean n) throws JasperException {
561             doSmap(n);
562             visitBody(n);
563         }
564
565         public void visit(Node.PlugIn n) throws JasperException {
566             doSmap(n);
567             visitBody(n);
568         }
569
570         public void visit(Node.CustomTag n) throws JasperException {
571             doSmap(n);
572             visitBody(n);
573         }
574
575         public void visit(Node.UninterpretedTag n) throws JasperException {
576             doSmap(n);
577             visitBody(n);
578         }
579
580         public void visit(Node.JspElement n) throws JasperException {
581             doSmap(n);
582             visitBody(n);
583         }
584
585         public void visit(Node.JspText n) throws JasperException {
586             doSmap(n);
587             visitBody(n);
588         }
589
590         public void visit(Node.NamedAttribute n) throws JasperException {
591             visitBody(n);
592         }
593
594         public void visit(Node.JspBody n) throws JasperException {
595             doSmap(n);
596             visitBody(n);
597         }
598
599         public void visit(Node.InvokeAction n) throws JasperException {
600             doSmap(n);
601             visitBody(n);
602         }
603
604         public void visit(Node.DoBodyAction n) throws JasperException {
605             doSmap(n);
606             visitBody(n);
607         }
608
609         public void visit(Node.ELExpression n) throws JasperException {
610             doSmap(n);
611         }
612
613         public void visit(Node.TemplateText n) throws JasperException {
614             Mark mark = n.getStart();
615             if (mark == null) {
616                 return;
617             }
618
619             //Add the file information
620
String JavaDoc fileName = mark.getFile();
621             smap.addFile(unqualify(fileName), fileName);
622
623             //Add a LineInfo that corresponds to the beginning of this node
624
int iInputStartLine = mark.getLineNumber();
625             int iOutputStartLine = n.getBeginJavaLine();
626             int iOutputLineIncrement = breakAtLF? 1: 0;
627             smap.addLineData(iInputStartLine, fileName, 1, iOutputStartLine,
628                              iOutputLineIncrement);
629
630             // Output additional mappings in the text
631
java.util.ArrayList JavaDoc extraSmap = n.getExtraSmap();
632
633             if (extraSmap != null) {
634                 for (int i = 0; i < extraSmap.size(); i++) {
635                     iOutputStartLine += iOutputLineIncrement;
636                     smap.addLineData(
637                         iInputStartLine+((Integer JavaDoc)extraSmap.get(i)).intValue(),
638                         fileName,
639                         1,
640                         iOutputStartLine,
641                         iOutputLineIncrement);
642                 }
643             }
644         }
645
646         private void doSmap(
647             Node n,
648             int inLineCount,
649             int outIncrement,
650             int skippedLines) {
651             Mark mark = n.getStart();
652             if (mark == null) {
653                 return;
654             }
655
656             String JavaDoc unqualifiedName = unqualify(mark.getFile());
657             smap.addFile(unqualifiedName, mark.getFile());
658             smap.addLineData(
659                 mark.getLineNumber() + skippedLines,
660                 mark.getFile(),
661                 inLineCount - skippedLines,
662                 n.getBeginJavaLine() + skippedLines,
663                 outIncrement);
664         }
665
666         private void doSmap(Node n) {
667             doSmap(n, 1, n.getEndJavaLine() - n.getBeginJavaLine(), 0);
668         }
669
670         private void doSmapText(Node n) {
671             String JavaDoc text = n.getText();
672             int index = 0;
673             int next = 0;
674             int lineCount = 1;
675             int skippedLines = 0;
676             boolean slashStarSeen = false;
677             boolean beginning = true;
678
679             // Count lines inside text, but skipping comment lines at the
680
// beginning of the text.
681
while ((next = text.indexOf('\n', index)) > -1) {
682                 if (beginning) {
683                     String JavaDoc line = text.substring(index, next).trim();
684                     if (!slashStarSeen && line.startsWith("/*")) {
685                         slashStarSeen = true;
686                     }
687                     if (slashStarSeen) {
688                         skippedLines++;
689                         int endIndex = line.indexOf("*/");
690                         if (endIndex >= 0) {
691                             // End of /* */ comment
692
slashStarSeen = false;
693                             if (endIndex < line.length() - 2) {
694                                 // Some executable code after comment
695
skippedLines--;
696                                 beginning = false;
697                             }
698                         }
699                     } else if (line.length() == 0 || line.startsWith("//")) {
700                         skippedLines++;
701                     } else {
702                         beginning = false;
703                     }
704                 }
705                 lineCount++;
706                 index = next + 1;
707             }
708
709             doSmap(n, lineCount, 1, skippedLines);
710         }
711     }
712
713     private static class PreScanVisitor extends Node.Visitor {
714
715         HashMap JavaDoc map = new HashMap JavaDoc();
716
717         public void doVisit(Node n) {
718             String JavaDoc inner = n.getInnerClassName();
719             if (inner != null && !map.containsKey(inner)) {
720                 map.put(inner, new SmapStratum("JSP"));
721             }
722         }
723
724         HashMap JavaDoc getMap() {
725             return map;
726         }
727     }
728     
729 }
730
Popular Tags