/** Create and compile an XPath expression object. */ public Object makeObject() throws Exception { if (logger.isDebugEnabled()) logger.debug("makeObject(" + xpathString + ")"); // Create context final IndependentContext independentContext = new XPathCacheStaticContext(allowAllVariables); // Set the base URI if specified if (baseURI != null) independentContext.setBaseURI(baseURI); // Declare namespaces if (prefixToURIMap != null) { for (final String prefix : prefixToURIMap.keySet()) { independentContext.declareNamespace(prefix, prefixToURIMap.get(prefix)); } } // Declare variables (we don't use the values here, just the names) final Map<String, Variable> variables = new HashMap<String, Variable>(); if (variableNames != null) { for (final String name : variableNames) { final Variable variable = independentContext.declareVariable(name); variable.setUseStack( true); // "Indicate that values of variables are to be found on the stack, not in the // Variable object itself" variables.put(name, variable); } } // Add function library if (functionLibrary != null) { // This is ok ((FunctionLibraryList) independentContext.getFunctionLibrary()) .libraryList.add(0, functionLibrary); } // Create and compile the expression try { final Expression expression; if (isAvt) { final Expression tempExpression = AttributeValueTemplate.make(xpathString, -1, independentContext); // Running typeCheck() is mandatory otherwise things break! This is also done when using // evaluator.createExpression() expression = tempExpression.typeCheck(independentContext, Type.ITEM_TYPE); } else { // We used to use XPathEvaluator.createExpression(), but there is a bug in it related to // slots allocation, so we do the work ourselves instead. final Expression tempExpression = ExpressionTool.make(xpathString, independentContext, 0, Token.EOF, 1); expression = tempExpression.typeCheck(independentContext, Type.ITEM_TYPE); } // Allocate variable slots in all cases ExpressionTool.allocateSlots( expression, independentContext.getStackFrameMap().getNumberOfVariables(), independentContext.getStackFrameMap()); { // Provide an Executable with the only purpose of allowing the evaluate() function find // the right // FunctionLibrary if (expression instanceof ComputedExpression) { final ComputedExpression computedExpression = (ComputedExpression) expression; computedExpression.setParentExpression( new Container() { public Executable getExecutable() { return new Executable() { { setFunctionLibrary(independentContext.getFunctionLibrary()); setLocationMap(new LocationMap()); setConfiguration(independentContext.getConfiguration()); } }; } public LocationProvider getLocationProvider() { return computedExpression.getLocationProvider(); } public int getHostLanguage() { return Configuration.JAVA_APPLICATION; } public boolean replaceSubExpression( Expression expression, Expression expression1) { return computedExpression.replaceSubExpression(expression, expression1); } public int getColumnNumber() { return (locationData != null) ? locationData.getCol() : -1; } public int getLineNumber() { return (locationData != null) ? locationData.getLine() : -1; } public String getPublicId() { return (locationData != null) ? locationData.getPublicID() : null; } public String getSystemId() { return (locationData != null) ? locationData.getSystemID() : null; } }); } } // TODO: For now only play with XForms expressions. But should decide probably based on // flag? if (false && functionLibrary == XFormsContainingDocument.getFunctionLibrary()) { final List<String> instanceIds = analyzeExpression(expression, xpathString); if (instanceIds == null) logger.info(" XXX EXPRESSION DEPENDS ON MORE THAN INSTANCES: " + xpathString); else { logger.info(" XXX EXPRESSION DEPENDS ON INSTANCES: " + xpathString); for (String instanceId : instanceIds) { logger.info(" instance: " + instanceId); } } } return new PooledXPathExpression(expression, pool, independentContext, variables); } catch (Throwable t) { throw new OXFException(t); } }
public static PooledXPathExpression getXPathExpression( PropertyContext propertyContext, List<Item> contextItems, int contextPosition, String xpathString, Map<String, String> prefixToURIMap, Map<String, ValueRepresentation> variableToValueMap, FunctionLibrary functionLibrary, String baseURI, boolean isAvt, boolean testNoCache, LocationData locationData) { try { // Find pool from cache final Long validity = (long) 0; final Cache cache = ObjectCache.instance(XPATH_CACHE_NAME, XPATH_CACHE_DEFAULT_SIZE); final FastStringBuffer cacheKeyString = new FastStringBuffer(xpathString); { if (functionLibrary != null) { // This is ok cacheKeyString.append('|'); cacheKeyString.append(Integer.toString(functionLibrary.hashCode())); } } { // NOTE: Mike Kay confirms on 2007-07-04 that compilation depends on the namespace context, // so we need // to use it as part of the cache key. // TODO: PERF: It turns out that this takes a lot of time. Now that the namespace // information is computed statically, we can do better. if (DEBUG_TEST_KEY_OPTIMIZATION) { // PERF TEST ONLY cacheKeyString.append("|DUMMYNSVAR|"); } else { if (prefixToURIMap != null) { final Map<String, String> sortedMap = (prefixToURIMap instanceof TreeMap) ? prefixToURIMap : new TreeMap<String, String>( prefixToURIMap); // this should make sure we always get the keys in the same // order for (Map.Entry<String, String> currentEntry : sortedMap.entrySet()) { cacheKeyString.append('|'); cacheKeyString.append(currentEntry.getKey()); cacheKeyString.append('='); cacheKeyString.append(currentEntry.getValue()); } } } } if (DEBUG_TEST_KEY_OPTIMIZATION) { // PERF TEST ONLY // NOP } else { if (variableToValueMap != null && variableToValueMap.size() > 0) { // There are some variables in scope. They must be part of the key // TODO: Put this in static state as this can be determined statically once and for all for (final String variableName : variableToValueMap.keySet()) { cacheKeyString.append('|'); cacheKeyString.append(variableName); } } } { // Add this to the key as evaluating "name" as XPath or as AVT is very different! cacheKeyString.append('|'); cacheKeyString.append(Boolean.toString(isAvt)); } // TODO: Add baseURI to cache key (currently, baseURI is pretty much unused) final Set<String> variableNames = (variableToValueMap != null) ? variableToValueMap.keySet() : null; final PooledXPathExpression expr; if (testNoCache) { // For testing only: don't get expression from cache final Object o = new XFormsCachePoolableObjetFactory( null, xpathString, prefixToURIMap, variableNames, functionLibrary, baseURI, isAvt, false, locationData) .makeObject(); expr = (PooledXPathExpression) o; } else { // Get or create pool final InternalCacheKey cacheKey = new InternalCacheKey("XPath Expression2", cacheKeyString.toString()); ObjectPool pool = (ObjectPool) cache.findValid(propertyContext, cacheKey, validity); if (pool == null) { pool = createXPathPool( xpathString, prefixToURIMap, variableNames, functionLibrary, baseURI, isAvt, locationData); cache.add(propertyContext, cacheKey, validity, pool); } // Get object from pool final Object o = pool.borrowObject(); expr = (PooledXPathExpression) o; } // Set context items and position expr.setContextItems(contextItems, contextPosition); // Set variables expr.setVariables(variableToValueMap); return expr; } catch (Exception e) { throw handleXPathException(e, xpathString, "preparing XPath expression", locationData); } }