1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  23 
  24 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  25 import com.sun.org.apache.bcel.internal.generic.GETSTATIC;
  26 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  27 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  28 import com.sun.org.apache.bcel.internal.generic.PUSH;
  29 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  32 
  33 /**
  34  * @author Jacek Ambroziak
  35  * @author Santiago Pericas-Geertsen
  36  * @author Morten Jorgensen
  37  */
  38 final class Text extends Instruction {
  39 
  40     private String _text;
  41     private boolean _escaping = true;
  42     private boolean _ignore = false;
  43     private boolean _textElement = false;
  44 
  45     /**
  46      * Create a blank Text syntax tree node.
  47      */
  48     public Text() {
  49         _textElement = true;
  50     }
  51 
  52     /**
  53      * Create text syntax tree node.
  54      * @param text is the text to put in the node.
  55      */
  56     public Text(String text) {
  57         _text = text;
  58     }
  59 
  60     /**
  61      * Returns the text wrapped inside this node
  62      * @return The text wrapped inside this node
  63      */
  64     protected String getText() {
  65         return _text;
  66     }
  67 
  68     /**
  69      * Set the text for this node. Appends the given text to any already
  70      * existing text (using string concatenation, so use only when needed).
  71      * @param text is the text to wrap inside this node.
  72      */
  73     protected void setText(String text) {
  74         if (_text == null)
  75             _text = text;
  76         else
  77             _text = _text + text;
  78     }
  79 
  80     public void display(int indent) {
  81         indent(indent);
  82         Util.println("Text");
  83         indent(indent + IndentIncrement);
  84         Util.println(_text);
  85     }
  86 
  87     public void parseContents(Parser parser) {
  88         final String str = getAttribute("disable-output-escaping");
  89         if ((str != null) && (str.equals("yes"))) _escaping = false;
  90 
  91         parseChildren(parser);
  92 
  93         if (_text == null) {
  94             if (_textElement) {
  95                 _text = EMPTYSTRING;
  96             }
  97             else {
  98                 _ignore = true;
  99             }
 100         }
 101         else if (_textElement) {
 102             if (_text.length() == 0) _ignore = true;
 103         }
 104         else if (getParent() instanceof LiteralElement) {
 105             LiteralElement element = (LiteralElement)getParent();
 106             String space = element.getAttribute("xml:space");
 107             if ((space == null) || (!space.equals("preserve")))
 108         {
 109             int i;
 110             final int textLength = _text.length();
 111             for (i = 0; i < textLength; i++) {
 112                 char c = _text.charAt(i);
 113                 if (!isWhitespace(c))
 114                     break;
 115             }
 116             if (i == textLength)
 117                 _ignore = true;
 118         }
 119         }
 120         else {
 121         int i;
 122         final int textLength = _text.length();
 123         for (i = 0; i < textLength; i++)
 124         {
 125             char c = _text.charAt(i);
 126             if (!isWhitespace(c))
 127                 break;
 128         }
 129         if (i == textLength)
 130             _ignore = true;
 131         }
 132     }
 133 
 134     public void ignore() {
 135         _ignore = true;
 136     }
 137 
 138     public boolean isIgnore() {
 139         return _ignore;
 140     }
 141 
 142     public boolean isTextElement() {
 143         return _textElement;
 144     }
 145 
 146     protected boolean contextDependent() {
 147         return false;
 148     }
 149 
 150     private static boolean isWhitespace(char c)
 151     {
 152         return (c == 0x20 || c == 0x09 || c == 0x0A || c == 0x0D);
 153     }
 154 
 155     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
 156         final ConstantPoolGen cpg = classGen.getConstantPool();
 157         final InstructionList il = methodGen.getInstructionList();
 158 
 159         if (!_ignore) {
 160             // Turn off character escaping if so is wanted.
 161             final int esc = cpg.addInterfaceMethodref(OUTPUT_HANDLER,
 162                                                       "setEscaping", "(Z)Z");
 163             if (!_escaping) {
 164                 il.append(methodGen.loadHandler());
 165                 il.append(new PUSH(cpg, false));
 166                 il.append(new INVOKEINTERFACE(esc, 2));
 167             }
 168 
 169             il.append(methodGen.loadHandler());
 170 
 171             // Call characters(String) or characters(char[],int,int), as
 172             // appropriate.
 173             if (!canLoadAsArrayOffsetLength()) {
 174                 final int characters = cpg.addInterfaceMethodref(OUTPUT_HANDLER,
 175                                                            "characters",
 176                                                            "("+STRING_SIG+")V");
 177                 il.append(new PUSH(cpg, _text));
 178                 il.append(new INVOKEINTERFACE(characters, 2));
 179             } else {
 180                 final int characters = cpg.addInterfaceMethodref(OUTPUT_HANDLER,
 181                                                                  "characters",
 182                                                                  "([CII)V");
 183                 loadAsArrayOffsetLength(classGen, methodGen);
 184                 il.append(new INVOKEINTERFACE(characters, 4));
 185             }
 186 
 187             // Restore character escaping setting to whatever it was.
 188             // Note: setEscaping(bool) returns the original (old) value
 189             if (!_escaping) {
 190                 il.append(methodGen.loadHandler());
 191                 il.append(SWAP);
 192                 il.append(new INVOKEINTERFACE(esc, 2));
 193                 il.append(POP);
 194             }
 195         }
 196         translateContents(classGen, methodGen);
 197     }
 198 
 199     /**
 200      * Check whether this Text node can be stored in a char[] in the translet.
 201      * Calling this is precondition to calling loadAsArrayOffsetLength.
 202      * @see #loadAsArrayOffsetLength(ClassGenerator,MethodGenerator)
 203      * @return true if this Text node can be
 204      */
 205     public boolean canLoadAsArrayOffsetLength() {
 206         // Magic number!  21845*3 == 65535.  BCEL uses a DataOutputStream to
 207         // serialize class files.  The Java run-time places a limit on the size
 208         // of String data written using a DataOutputStream - it cannot require
 209         // more than 64KB when represented as UTF-8.  The number of bytes
 210         // required to represent a Java string as UTF-8 cannot be greater
 211         // than three times the number of char's in the string, hence the
 212         // check for 21845.
 213 
 214         return (_text.length() <= 21845);
 215     }
 216 
 217     /**
 218      * Generates code that loads the array that will contain the character
 219      * data represented by this Text node, followed by the offset of the
 220      * data from the start of the array, and then the length of the data.
 221      *
 222      * The pre-condition to calling this method is that
 223      * canLoadAsArrayOffsetLength() returns true.
 224      * @see #canLoadArrayOffsetLength()
 225      */
 226     public void loadAsArrayOffsetLength(ClassGenerator classGen,
 227                                         MethodGenerator methodGen) {
 228         final ConstantPoolGen cpg = classGen.getConstantPool();
 229         final InstructionList il = methodGen.getInstructionList();
 230         final XSLTC xsltc = classGen.getParser().getXSLTC();
 231 
 232         // The XSLTC object keeps track of character data
 233         // that is to be stored in char arrays.
 234         final int offset = xsltc.addCharacterData(_text);
 235         final int length = _text.length();
 236         String charDataFieldName =
 237             STATIC_CHAR_DATA_FIELD + (xsltc.getCharacterDataCount()-1);
 238 
 239         il.append(new GETSTATIC(cpg.addFieldref(xsltc.getClassName(),
 240                                        charDataFieldName,
 241                                        STATIC_CHAR_DATA_FIELD_SIG)));
 242         il.append(new PUSH(cpg, offset));
 243         il.append(new PUSH(cpg, _text.length()));
 244     }
 245 }