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: ExtensionHandlerGeneral.java 469672 2006-10-31 21:56:19Z minchau $
020 */
021 package org.apache.xalan.extensions;
022
023 import java.io.IOException;
024 import java.io.InputStream;
025 import java.lang.reflect.Method;
026 import java.net.URL;
027 import java.net.URLConnection;
028 import java.util.Hashtable;
029 import java.util.Vector;
030
031 import javax.xml.transform.TransformerException;
032
033 import org.apache.xml.res.XMLErrorResources;
034 import org.apache.xml.res.XMLMessages;
035
036 import org.apache.xalan.res.XSLMessages;
037 import org.apache.xalan.res.XSLTErrorResources;
038 import org.apache.xalan.templates.ElemTemplateElement;
039 import org.apache.xalan.templates.Stylesheet;
040 import org.apache.xalan.transformer.TransformerImpl;
041 import org.apache.xml.dtm.DTMIterator;
042 import org.apache.xml.dtm.ref.DTMNodeList;
043 import org.apache.xml.utils.StringVector;
044 import org.apache.xml.utils.SystemIDResolver;
045 import org.apache.xpath.XPathProcessorException;
046 import org.apache.xpath.functions.FuncExtFunction;
047 import org.apache.xpath.objects.XObject;
048
049 /**
050 * Class handling an extension namespace for XPath. Provides functions
051 * to test a function's existence and call a function
052 *
053 * @author Sanjiva Weerawarana (sanjiva@watson.ibm.com)
054 * @xsl.usage internal
055 */
056 public class ExtensionHandlerGeneral extends ExtensionHandler
057 {
058
059 /** script source to run (if any) */
060 private String m_scriptSrc;
061
062 /** URL of source of script (if any) */
063 private String m_scriptSrcURL;
064
065 /** functions of namespace */
066 private Hashtable m_functions = new Hashtable();
067
068 /** elements of namespace */
069 private Hashtable m_elements = new Hashtable();
070
071 // BSF objects used to invoke BSF by reflection. Do not import the BSF classes
072 // since we don't want a compile dependency on BSF.
073
074 /** BSF manager used to run scripts */
075 private Object m_engine;
076
077 /** Engine call to invoke scripts */
078 private Method m_engineCall = null;
079
080 // static fields
081
082 /** BSFManager package name */
083 private static String BSF_MANAGER ;
084
085 /** Default BSFManager name */
086 private static final String DEFAULT_BSF_MANAGER = "org.apache.bsf.BSFManager";
087
088 /** Property name to load the BSFManager class */
089 private static final String propName = "org.apache.xalan.extensions.bsf.BSFManager";
090
091 /** Integer Zero */
092 private static final Integer ZEROINT = new Integer(0);
093
094 static{
095 BSF_MANAGER = ObjectFactory.lookUpFactoryClassName(propName, null, null);
096
097 if (BSF_MANAGER == null){
098 BSF_MANAGER = DEFAULT_BSF_MANAGER;
099 }
100 }
101
102 /**
103 * Construct a new extension namespace handler given all the information
104 * needed.
105 *
106 * @param namespaceUri the extension namespace URI that I'm implementing
107 * @param elemNames Vector of element names
108 * @param funcNames string containing list of functions of extension NS
109 * @param scriptLang Scripting language of implementation
110 * @param scriptSrcURL URL of source script
111 * @param scriptSrc the actual script code (if any)
112 * @param systemId
113 *
114 * @throws TransformerException
115 */
116 public ExtensionHandlerGeneral(
117 String namespaceUri, StringVector elemNames, StringVector funcNames, String scriptLang, String scriptSrcURL, String scriptSrc, String systemId)
118 throws TransformerException
119 {
120
121 super(namespaceUri, scriptLang);
122
123 if (elemNames != null)
124 {
125 Object junk = new Object();
126 int n = elemNames.size();
127
128 for (int i = 0; i < n; i++)
129 {
130 String tok = elemNames.elementAt(i);
131
132 m_elements.put(tok, junk); // just stick it in there basically
133 }
134 }
135
136 if (funcNames != null)
137 {
138 Object junk = new Object();
139 int n = funcNames.size();
140
141 for (int i = 0; i < n; i++)
142 {
143 String tok = funcNames.elementAt(i);
144
145 m_functions.put(tok, junk); // just stick it in there basically
146 }
147 }
148
149 m_scriptSrcURL = scriptSrcURL;
150 m_scriptSrc = scriptSrc;
151
152 if (m_scriptSrcURL != null)
153 {
154 URL url = null;
155 try{
156 url = new URL(m_scriptSrcURL);
157 }
158 catch (java.net.MalformedURLException mue)
159 {
160 int indexOfColon = m_scriptSrcURL.indexOf(':');
161 int indexOfSlash = m_scriptSrcURL.indexOf('/');
162
163 if ((indexOfColon != -1) && (indexOfSlash != -1)
164 && (indexOfColon < indexOfSlash))
165 {
166 // The url is absolute.
167 url = null;
168 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_COULD_NOT_FIND_EXTERN_SCRIPT, new Object[]{m_scriptSrcURL}), mue); //"src attribute not yet supported for "
169 //+ scriptLang);
170 }
171 else
172 {
173 try{
174 url = new URL(new URL(SystemIDResolver.getAbsoluteURI(systemId)), m_scriptSrcURL);
175 }
176 catch (java.net.MalformedURLException mue2)
177 {
178 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_COULD_NOT_FIND_EXTERN_SCRIPT, new Object[]{m_scriptSrcURL}), mue2); //"src attribute not yet supported for "
179 //+ scriptLang);
180 }
181 }
182 }
183 if (url != null)
184 {
185 try
186 {
187 URLConnection uc = url.openConnection();
188 InputStream is = uc.getInputStream();
189 byte []bArray = new byte[uc.getContentLength()];
190 is.read(bArray);
191 m_scriptSrc = new String(bArray);
192
193 }
194 catch (IOException ioe)
195 {
196 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_COULD_NOT_FIND_EXTERN_SCRIPT, new Object[]{m_scriptSrcURL}), ioe); //"src attribute not yet supported for "
197 //+ scriptLang);
198 }
199 }
200
201 }
202
203 Object manager = null;
204 try
205 {
206 manager = ObjectFactory.newInstance(
207 BSF_MANAGER, ObjectFactory.findClassLoader(), true);
208 }
209 catch (ObjectFactory.ConfigurationError e)
210 {
211 e.printStackTrace();
212 }
213
214 if (manager == null)
215 {
216 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_INIT_BSFMGR, null)); //"Could not initialize BSF manager");
217 }
218
219 try
220 {
221 Method loadScriptingEngine = manager.getClass()
222 .getMethod("loadScriptingEngine", new Class[]{ String.class });
223
224 m_engine = loadScriptingEngine.invoke(manager,
225 new Object[]{ scriptLang });
226
227 Method engineExec = m_engine.getClass().getMethod("exec",
228 new Class[]{ String.class, Integer.TYPE, Integer.TYPE, Object.class });
229
230 // "Compile" the program
231 engineExec.invoke(m_engine,
232 new Object[]{ "XalanScript", ZEROINT, ZEROINT, m_scriptSrc });
233 }
234 catch (Exception e)
235 {
236 e.printStackTrace();
237
238 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_CMPL_EXTENSN, null), e); //"Could not compile extension", e);
239 }
240 }
241
242 /**
243 * Tests whether a certain function name is known within this namespace.
244 * @param function name of the function being tested
245 * @return true if its known, false if not.
246 */
247 public boolean isFunctionAvailable(String function)
248 {
249 return (m_functions.get(function) != null);
250 }
251
252 /**
253 * Tests whether a certain element name is known within this namespace.
254 * @param element name of the element being tested
255 * @return true if its known, false if not.
256 */
257 public boolean isElementAvailable(String element)
258 {
259 return (m_elements.get(element) != null);
260 }
261
262 /**
263 * Process a call to a function.
264 *
265 * @param funcName Function name.
266 * @param args The arguments of the function call.
267 * @param methodKey A key that uniquely identifies this class and method call.
268 * @param exprContext The context in which this expression is being executed.
269 *
270 * @return the return value of the function evaluation.
271 *
272 * @throws TransformerException if parsing trouble
273 */
274 public Object callFunction(
275 String funcName, Vector args, Object methodKey, ExpressionContext exprContext)
276 throws TransformerException
277 {
278
279 Object[] argArray;
280
281 try
282 {
283 argArray = new Object[args.size()];
284
285 for (int i = 0; i < argArray.length; i++)
286 {
287 Object o = args.get(i);
288
289 argArray[i] = (o instanceof XObject) ? ((XObject) o).object() : o;
290 o = argArray[i];
291 if(null != o && o instanceof DTMIterator)
292 {
293 argArray[i] = new DTMNodeList((DTMIterator)o);
294 }
295 }
296
297 if (m_engineCall == null) {
298 m_engineCall = m_engine.getClass().getMethod("call",
299 new Class[]{ Object.class, String.class, Object[].class });
300 }
301
302 return m_engineCall.invoke(m_engine,
303 new Object[]{ null, funcName, argArray });
304 }
305 catch (Exception e)
306 {
307 e.printStackTrace();
308
309 String msg = e.getMessage();
310
311 if (null != msg)
312 {
313 if (msg.startsWith("Stopping after fatal error:"))
314 {
315 msg = msg.substring("Stopping after fatal error:".length());
316 }
317
318 // System.out.println("Call to extension function failed: "+msg);
319 throw new TransformerException(e);
320 }
321 else
322 {
323
324 // Should probably make a TRaX Extension Exception.
325 throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_CREATE_EXTENSN, new Object[]{funcName, e })); //"Could not create extension: " + funcName
326 //+ " because of: " + e);
327 }
328 }
329 }
330
331 /**
332 * Process a call to an XPath extension function
333 *
334 * @param extFunction The XPath extension function
335 * @param args The arguments of the function call.
336 * @param exprContext The context in which this expression is being executed.
337 * @return the return value of the function evaluation.
338 * @throws TransformerException
339 */
340 public Object callFunction(FuncExtFunction extFunction,
341 Vector args,
342 ExpressionContext exprContext)
343 throws TransformerException
344 {
345 return callFunction(extFunction.getFunctionName(), args,
346 extFunction.getMethodKey(), exprContext);
347 }
348
349 /**
350 * Process a call to this extension namespace via an element. As a side
351 * effect, the results are sent to the TransformerImpl's result tree.
352 *
353 * @param localPart Element name's local part.
354 * @param element The extension element being processed.
355 * @param transformer Handle to TransformerImpl.
356 * @param stylesheetTree The compiled stylesheet tree.
357 * @param methodKey A key that uniquely identifies this class and method call.
358 *
359 * @throws XSLProcessorException thrown if something goes wrong
360 * while running the extension handler.
361 * @throws MalformedURLException if loading trouble
362 * @throws FileNotFoundException if loading trouble
363 * @throws IOException if loading trouble
364 * @throws TransformerException if parsing trouble
365 */
366 public void processElement(
367 String localPart, ElemTemplateElement element, TransformerImpl transformer,
368 Stylesheet stylesheetTree, Object methodKey)
369 throws TransformerException, IOException
370 {
371
372 Object result = null;
373 XSLProcessorContext xpc = new XSLProcessorContext(transformer, stylesheetTree);
374
375 try
376 {
377 Vector argv = new Vector(2);
378
379 argv.add(xpc);
380 argv.add(element);
381
382 result = callFunction(localPart, argv, methodKey,
383 transformer.getXPathContext().getExpressionContext());
384 }
385 catch (XPathProcessorException e)
386 {
387
388 // e.printStackTrace ();
389 throw new TransformerException(e.getMessage(), e);
390 }
391
392 if (result != null)
393 {
394 xpc.outputToResultTree(stylesheetTree, result);
395 }
396 }
397 }