001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: Text.java 468650 2006-10-28 07:03:30Z minchau $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import org.apache.bcel.generic.ConstantPoolGen; 025 import org.apache.bcel.generic.GETSTATIC; 026 import org.apache.bcel.generic.INVOKEINTERFACE; 027 import org.apache.bcel.generic.InstructionList; 028 import org.apache.bcel.generic.PUSH; 029 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 030 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 031 import org.apache.xalan.xsltc.compiler.util.Util; 032 033 /** 034 * @author Jacek Ambroziak 035 * @author Santiago Pericas-Geertsen 036 * @author Morten Jorgensen 037 */ 038 final class Text extends Instruction { 039 040 private String _text; 041 private boolean _escaping = true; 042 private boolean _ignore = false; 043 private boolean _textElement = false; 044 045 /** 046 * Create a blank Text syntax tree node. 047 */ 048 public Text() { 049 _textElement = true; 050 } 051 052 /** 053 * Create text syntax tree node. 054 * @param text is the text to put in the node. 055 */ 056 public Text(String text) { 057 _text = text; 058 } 059 060 /** 061 * Returns the text wrapped inside this node 062 * @return The text wrapped inside this node 063 */ 064 protected String getText() { 065 return _text; 066 } 067 068 /** 069 * Set the text for this node. Appends the given text to any already 070 * existing text (using string concatenation, so use only when needed). 071 * @param text is the text to wrap inside this node. 072 */ 073 protected void setText(String text) { 074 if (_text == null) 075 _text = text; 076 else 077 _text = _text + text; 078 } 079 080 public void display(int indent) { 081 indent(indent); 082 Util.println("Text"); 083 indent(indent + IndentIncrement); 084 Util.println(_text); 085 } 086 087 public void parseContents(Parser parser) { 088 final String str = getAttribute("disable-output-escaping"); 089 if ((str != null) && (str.equals("yes"))) _escaping = false; 090 091 parseChildren(parser); 092 093 if (_text == null) { 094 if (_textElement) { 095 _text = EMPTYSTRING; 096 } 097 else { 098 _ignore = true; 099 } 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 }