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