Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » jasper » compiler » [javadoc | source]
    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;
   21   import java.io.FileInputStream;
   22   import java.io.FileNotFoundException;
   23   import java.io.FileOutputStream;
   24   import java.io.IOException;
   25   import java.io.OutputStreamWriter;
   26   import java.io.PrintWriter;
   27   import java.io.UnsupportedEncodingException;
   28   import java.util.HashMap;
   29   import java.util.Iterator;
   30   import java.util.Map;
   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.juli.logging.Log log=
   48           org.apache.juli.logging.LogFactory.getLog( SmapUtil.class );
   49   
   50       //*********************************************************************
   51       // Constants
   52   
   53       public static final String 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[] generateSmap(
   67           JspCompilationContext ctxt,
   68           Node.Nodes pageNodes)
   69           throws IOException {
   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 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 outSmap = new File(ctxt.getClassFileName() + ".smap");
  110               PrintWriter so =
  111                   new PrintWriter(
  112                       new OutputStreamWriter(
  113                           new FileOutputStream(outSmap),
  114                           SMAP_ENCODING));
  115               so.print(g.getString());
  116               so.close();
  117           }
  118   
  119           String classFileName = ctxt.getClassFileName();
  120           int innerClassCount = map.size();
  121           String [] smapInfo = new String[2 + innerClassCount*2];
  122           smapInfo[0] = classFileName;
  123           smapInfo[1] = g.getString();
  124   
  125           int count = 2;
  126           Iterator iter = map.entrySet().iterator();
  127           while (iter.hasNext()) {
  128               Map.Entry entry = (Map.Entry) iter.next();
  129               String innerClass = (String) 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 innerClassFileName =
  137                   classFileName.substring(0, classFileName.indexOf(".class")) +
  138                   '$' + innerClass + ".class";
  139               if (ctxt.getOptions().isSmapDumped()) {
  140                   File outSmap = new File(innerClassFileName + ".smap");
  141                   PrintWriter so =
  142                       new PrintWriter(
  143                           new OutputStreamWriter(
  144                               new FileOutputStream(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[] smap)
  158           throws IOException {
  159           if (smap == null) {
  160               return;
  161           }
  162   
  163           for (int i = 0; i < smap.length; i += 2) {
  164               File outServlet = new File(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 unqualify(String 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 inputSmapPath(String 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.juli.logging.Log log=
  193               org.apache.juli.logging.LogFactory.getLog( SDEInstaller.class );
  194   
  195           static final String 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[] args) throws IOException {
  207               if (args.length == 2) {
  208                   install(new File(args[0]), new File(args[1]));
  209               } else if (args.length == 3) {
  210                   install(
  211                       new File(args[0]),
  212                       new File(args[1]),
  213                       new File(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 inClassFile, File attrFile, File outClassFile)
  223               throws IOException {
  224               new SDEInstaller(inClassFile, attrFile, outClassFile);
  225           }
  226   
  227           static void install(File inOutClassFile, File attrFile)
  228               throws IOException {
  229               File tmpFile = new File(inOutClassFile.getPath() + "tmp");
  230               new SDEInstaller(inOutClassFile, attrFile, tmpFile);
  231               if (!inOutClassFile.delete()) {
  232                   throw new IOException("inOutClassFile.delete() failed");
  233               }
  234               if (!tmpFile.renameTo(inOutClassFile)) {
  235                   throw new IOException("tmpFile.renameTo(inOutClassFile) failed");
  236               }
  237           }
  238   
  239           static void install(File classFile, byte[] smap) throws IOException {
  240               File tmpFile = new File(classFile.getPath() + "tmp");
  241               new SDEInstaller(classFile, smap, tmpFile);
  242               if (!classFile.delete()) {
  243                   throw new IOException("classFile.delete() failed");
  244               }
  245               if (!tmpFile.renameTo(classFile)) {
  246                   throw new IOException("tmpFile.renameTo(classFile) failed");
  247               }
  248           }
  249   
  250           SDEInstaller(File inClassFile, byte[] sdeAttr, File outClassFile)
  251               throws IOException {
  252               if (!inClassFile.exists()) {
  253                   throw new FileNotFoundException("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 outStream = new FileOutputStream(outClassFile);
  266               outStream.write(gen, 0, genPos);
  267               outStream.close();
  268           }
  269   
  270           SDEInstaller(File inClassFile, File attrFile, File outClassFile)
  271               throws IOException {
  272               this(inClassFile, readWhole(attrFile), outClassFile);
  273           }
  274   
  275           static byte[] readWhole(File input) throws IOException {
  276               FileInputStream inStream = new FileInputStream(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("expected size: " + len);
  281               }
  282               inStream.close();
  283               return bytes;
  284           }
  285   
  286           void addSDE() throws UnsupportedEncodingException, IOException {
  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, IOException {
  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 str = new String(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("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 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 innerClassMap;
  511   
  512           SmapGenVisitor(SmapStratum s, boolean breakAtLF, HashMap 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 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 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 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)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 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 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 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 map = new HashMap();
  716   
  717           public void doVisit(Node n) {
  718               String inner = n.getInnerClassName();
  719               if (inner != null && !map.containsKey(inner)) {
  720                   map.put(inner, new SmapStratum("JSP"));
  721               }
  722           }
  723   
  724           HashMap getMap() {
  725               return map;
  726           }
  727       }
  728       
  729   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » jasper » compiler » [javadoc | source]