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      */
Text()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      */
Text(String text)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      */
getText()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      */
setText(String text)73     protected void setText(String text) {
74         if (_text == null)
75             _text = text;
76         else
77             _text = _text + text;
78     }
79 
display(int indent)80     public void display(int indent) {
81         indent(indent);
82         Util.println("Text");
83         indent(indent + IndentIncrement);
84         Util.println(_text);
85     }
86 
parseContents(Parser parser)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 
ignore()134     public void ignore() {
135         _ignore = true;
136     }
137 
isIgnore()138     public boolean isIgnore() {
139         return _ignore;
140     }
141 
isTextElement()142     public boolean isTextElement() {
143         return _textElement;
144     }
145 
contextDependent()146     protected boolean contextDependent() {
147         return false;
148     }
149 
isWhitespace(char c)150     private static boolean isWhitespace(char c)
151     {
152         return (c == 0x20 || c == 0x09 || c == 0x0A || c == 0x0D);
153     }
154 
translate(ClassGenerator classGen, MethodGenerator methodGen)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      */
canLoadAsArrayOffsetLength()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      */
loadAsArrayOffsetLength(ClassGenerator classGen, MethodGenerator methodGen)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 }
246