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 }