http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/beans/_MethodUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/beans/_MethodUtil.java b/src/main/java/freemarker/ext/beans/_MethodUtil.java deleted file mode 100644 index 8eed9f2..0000000 --- a/src/main/java/freemarker/ext/beans/_MethodUtil.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package freemarker.ext.beans; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.HashSet; -import java.util.Set; - -import freemarker.core.BugException; -import freemarker.core._DelayedConversionToString; -import freemarker.core._DelayedJQuote; -import freemarker.core._TemplateModelException; -import freemarker.template.TemplateModelException; -import freemarker.template.utility.ClassUtil; - -/** - * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! - * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can - * access things inside this package that users shouldn't. - */ -public final class _MethodUtil { - - private _MethodUtil() { - // Not meant to be instantiated - } - - /** - * Determines whether the type given as the 1st argument is convertible to the type given as the 2nd argument - * for method call argument conversion. This follows the rules of the Java reflection-based method call, except - * that since we don't have the value here, a boxed class is never seen as convertible to a primitive type. - * - * @return 0 means {@code false}, non-0 means {@code true}. - * That is, 0 is returned less specificity or incomparable specificity, also when if - * then method was aborted because of {@code ifHigherThan}. - * The absolute value of the returned non-0 number symbolizes how more specific it is: - * <ul> - * <li>1: The two classes are identical</li> - * <li>2: The 1st type is primitive, the 2nd type is the corresponding boxing class</li> - * <li>3: Both classes are numerical, and one is convertible into the other with widening conversion. - * E.g., {@code int} is convertible to {@code long} and {#code double}, hence {@code int} is more - * specific. - * This ignores primitive VS boxed mismatches, except that a boxed class is never seen as - * convertible to a primitive class.</li> - * <li>4: One class is {@code instanceof} of the other, but they aren't identical. - * But unlike in Java, primitive numerical types are {@code instanceof} {@link Number} here.</li> - * </ul> - */ - public static int isMoreOrSameSpecificParameterType(final Class specific, final Class generic, boolean bugfixed, - int ifHigherThan) { - if (ifHigherThan >= 4) return 0; - if (generic.isAssignableFrom(specific)) { - // Identity or widening reference conversion: - return generic == specific ? 1 : 4; - } else { - final boolean specificIsPrim = specific.isPrimitive(); - final boolean genericIsPrim = generic.isPrimitive(); - if (specificIsPrim) { - if (genericIsPrim) { - if (ifHigherThan >= 3) return 0; - return isWideningPrimitiveNumberConversion(specific, generic) ? 3 : 0; - } else { // => specificIsPrim && !genericIsPrim - if (bugfixed) { - final Class specificAsBoxed = ClassUtil.primitiveClassToBoxingClass(specific); - if (specificAsBoxed == generic) { - // A primitive class is more specific than its boxing class, because it can't store null - return 2; - } else if (generic.isAssignableFrom(specificAsBoxed)) { - // Note: This only occurs if `specific` is a primitive numerical, and `generic == Number` - return 4; - } else if (ifHigherThan >= 3) { - return 0; - } else if (Number.class.isAssignableFrom(specificAsBoxed) - && Number.class.isAssignableFrom(generic)) { - return isWideningBoxedNumberConversion(specificAsBoxed, generic) ? 3 : 0; - } else { - return 0; - } - } else { - return 0; - } - } - } else { // => !specificIsPrim - if (ifHigherThan >= 3) return 0; - if (bugfixed && !genericIsPrim - && Number.class.isAssignableFrom(specific) && Number.class.isAssignableFrom(generic)) { - return isWideningBoxedNumberConversion(specific, generic) ? 3 : 0; - } else { - return 0; - } - } - } // of: !generic.isAssignableFrom(specific) - } - - private static boolean isWideningPrimitiveNumberConversion(final Class source, final Class target) { - if (target == Short.TYPE && (source == Byte.TYPE)) { - return true; - } else if (target == Integer.TYPE && - (source == Short.TYPE || source == Byte.TYPE)) { - return true; - } else if (target == Long.TYPE && - (source == Integer.TYPE || source == Short.TYPE || - source == Byte.TYPE)) { - return true; - } else if (target == Float.TYPE && - (source == Long.TYPE || source == Integer.TYPE || - source == Short.TYPE || source == Byte.TYPE)) { - return true; - } else if (target == Double.TYPE && - (source == Float.TYPE || source == Long.TYPE || - source == Integer.TYPE || source == Short.TYPE || - source == Byte.TYPE)) { - return true; - } else { - return false; - } - } - - private static boolean isWideningBoxedNumberConversion(final Class source, final Class target) { - if (target == Short.class && source == Byte.class) { - return true; - } else if (target == Integer.class && - (source == Short.class || source == Byte.class)) { - return true; - } else if (target == Long.class && - (source == Integer.class || source == Short.class || - source == Byte.class)) { - return true; - } else if (target == Float.class && - (source == Long.class || source == Integer.class || - source == Short.class || source == Byte.class)) { - return true; - } else if (target == Double.class && - (source == Float.class || source == Long.class || - source == Integer.class || source == Short.class || - source == Byte.class)) { - return true; - } else { - return false; - } - } - - /** - * Attention, this doesn't handle primitive classes correctly, nor numerical conversions. - */ - public static Set getAssignables(Class c1, Class c2) { - Set s = new HashSet(); - collectAssignables(c1, c2, s); - return s; - } - - private static void collectAssignables(Class c1, Class c2, Set s) { - if (c1.isAssignableFrom(c2)) { - s.add(c1); - } - Class sc = c1.getSuperclass(); - if (sc != null) { - collectAssignables(sc, c2, s); - } - Class[] itf = c1.getInterfaces(); - for (int i = 0; i < itf.length; ++i) { - collectAssignables(itf[i], c2, s); - } - } - - public static Class[] getParameterTypes(Member member) { - if (member instanceof Method) { - return ((Method) member).getParameterTypes(); - } - if (member instanceof Constructor) { - return ((Constructor) member).getParameterTypes(); - } - throw new IllegalArgumentException("\"member\" must be Method or Constructor"); - } - - public static boolean isVarargs(Member member) { - if (member instanceof Method) { - return ((Method) member).isVarArgs(); - } - if (member instanceof Constructor) { - return ((Constructor) member).isVarArgs(); - } - throw new BugException(); - } - - /** - * Returns a more streamlined method or constructor description than {@code Member.toString()} does. - */ - public static String toString(Member member) { - if (!(member instanceof Method || member instanceof Constructor)) { - throw new IllegalArgumentException("\"member\" must be a Method or Constructor"); - } - - StringBuilder sb = new StringBuilder(); - - if ((member.getModifiers() & Modifier.STATIC) != 0) { - sb.append("static "); - } - - String className = ClassUtil.getShortClassName(member.getDeclaringClass()); - if (className != null) { - sb.append(className); - sb.append('.'); - } - sb.append(member.getName()); - - sb.append('('); - Class[] paramTypes = _MethodUtil.getParameterTypes(member); - for (int i = 0; i < paramTypes.length; i++) { - if (i != 0) sb.append(", "); - String paramTypeDecl = ClassUtil.getShortClassName(paramTypes[i]); - if (i == paramTypes.length - 1 && paramTypeDecl.endsWith("[]") && _MethodUtil.isVarargs(member)) { - sb.append(paramTypeDecl.substring(0, paramTypeDecl.length() - 2)); - sb.append("..."); - } else { - sb.append(paramTypeDecl); - } - } - sb.append(')'); - - return sb.toString(); - } - - public static Object[] invocationErrorMessageStart(Member member) { - return invocationErrorMessageStart(member, member instanceof Constructor); - } - - private static Object[] invocationErrorMessageStart(Object member, boolean isConstructor) { - return new Object[] { "Java ", isConstructor ? "constructor " : "method ", new _DelayedJQuote(member) }; - } - - public static TemplateModelException newInvocationTemplateModelException(Object object, Member member, Throwable e) { - return newInvocationTemplateModelException( - object, - member, - (member.getModifiers() & Modifier.STATIC) != 0, - member instanceof Constructor, - e); - } - - public static TemplateModelException newInvocationTemplateModelException(Object object, CallableMemberDescriptor callableMemberDescriptor, Throwable e) { - return newInvocationTemplateModelException( - object, - new _DelayedConversionToString(callableMemberDescriptor) { - @Override - protected String doConversion(Object callableMemberDescriptor) { - return ((CallableMemberDescriptor) callableMemberDescriptor).getDeclaration(); - } - }, - callableMemberDescriptor.isStatic(), - callableMemberDescriptor.isConstructor(), - e); - } - - private static TemplateModelException newInvocationTemplateModelException( - Object parentObject, Object member, boolean isStatic, boolean isConstructor, Throwable e) { - while (e instanceof InvocationTargetException) { - Throwable cause = ((InvocationTargetException) e).getTargetException(); - if (cause != null) { - e = cause; - } else { - break; - } - } - - return new _TemplateModelException(e, - invocationErrorMessageStart(member, isConstructor), - " threw an exception", - isStatic || isConstructor ? (Object) "" : new Object[] { - " when invoked on ", parentObject.getClass(), " object ", new _DelayedJQuote(parentObject) - }, - "; see cause exception in the Java stack trace."); - } - -} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/beans/package.html ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/beans/package.html b/src/main/java/freemarker/ext/beans/package.html deleted file mode 100644 index 2032969..0000000 --- a/src/main/java/freemarker/ext/beans/package.html +++ /dev/null @@ -1,40 +0,0 @@ -<!-- - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> -<html> -<head> -<title></title> -</head> -<body> - -<p>The {@link freemarker.template.DefaultObjectWrapper default object wrapper} of FreeMarker uses -this to expose Java Beans and POJO-s to templates, but it can also be used in itself as a -better alternative {@link freemarker.template.ObjectWrapper}.</p> - -<p>Most of the issues dealing with beans are handled by the -{@link freemarker.ext.beans.BeansWrapper#wrap(Object)}and {@link -freemarker.ext.beans.BeansWrapper#getStaticModels()} methods. In normal cases, - these are the only methods -you should use to turn an arbitrary Java object into a -FreeMarker {@link freemarker.template.TemplateModel}. Additionally, you can manually create -instance of any wrapper class using its constructors. -Note, however that in such cases you bypass the eventual model caching -of the wrapper.</p> - -</body> -</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/AtAtKey.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/AtAtKey.java b/src/main/java/freemarker/ext/dom/AtAtKey.java deleted file mode 100644 index 6efc127..0000000 --- a/src/main/java/freemarker/ext/dom/AtAtKey.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package freemarker.ext.dom; - -/** - * The special hash keys that start with "@@". - */ -enum AtAtKey { - - MARKUP("@@markup"), - NESTED_MARKUP("@@nested_markup"), - ATTRIBUTES_MARKUP("@@attributes_markup"), - TEXT("@@text"), - START_TAG("@@start_tag"), - END_TAG("@@end_tag"), - QNAME("@@qname"), - NAMESPACE("@@namespace"), - LOCAL_NAME("@@local_name"), - ATTRIBUTES("@@"), - PREVIOUS_SIBLING_ELEMENT("@@previous_sibling_element"), - NEXT_SIBLING_ELEMENT("@@next_sibling_element"); - - private final String key; - - public String getKey() { - return key; - } - - private AtAtKey(String key) { - this.key = key; - } - - public static boolean containsKey(String key) { - for (AtAtKey item : AtAtKey.values()) { - if (item.getKey().equals(key)) { - return true; - } - } - return false; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/AttributeNodeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/AttributeNodeModel.java b/src/main/java/freemarker/ext/dom/AttributeNodeModel.java deleted file mode 100644 index 8b49a77..0000000 --- a/src/main/java/freemarker/ext/dom/AttributeNodeModel.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package freemarker.ext.dom; - -import org.w3c.dom.Attr; - -import freemarker.core.Environment; -import freemarker.template.TemplateScalarModel; - -class AttributeNodeModel extends NodeModel implements TemplateScalarModel { - - public AttributeNodeModel(Attr att) { - super(att); - } - - public String getAsString() { - return ((Attr) node).getValue(); - } - - public String getNodeName() { - String result = node.getLocalName(); - if (result == null || result.equals("")) { - result = node.getNodeName(); - } - return result; - } - - public boolean isEmpty() { - return true; - } - - @Override - String getQualifiedName() { - String nsURI = node.getNamespaceURI(); - if (nsURI == null || nsURI.equals("")) - return node.getNodeName(); - Environment env = Environment.getCurrentEnvironment(); - String defaultNS = env.getDefaultNS(); - String prefix = null; - if (nsURI.equals(defaultNS)) { - prefix = "D"; - } else { - prefix = env.getPrefixForNamespace(nsURI); - } - if (prefix == null) { - return null; - } - return prefix + ":" + node.getLocalName(); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/CharacterDataNodeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/CharacterDataNodeModel.java b/src/main/java/freemarker/ext/dom/CharacterDataNodeModel.java deleted file mode 100644 index 36ce03c..0000000 --- a/src/main/java/freemarker/ext/dom/CharacterDataNodeModel.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package freemarker.ext.dom; - -import org.w3c.dom.CharacterData; -import org.w3c.dom.Comment; - -import freemarker.template.TemplateScalarModel; - -class CharacterDataNodeModel extends NodeModel implements TemplateScalarModel { - - public CharacterDataNodeModel(CharacterData text) { - super(text); - } - - public String getAsString() { - return ((org.w3c.dom.CharacterData) node).getData(); - } - - public String getNodeName() { - return (node instanceof Comment) ? "@comment" : "@text"; - } - - public boolean isEmpty() { - return true; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/DocumentModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/DocumentModel.java b/src/main/java/freemarker/ext/dom/DocumentModel.java deleted file mode 100644 index d1ad828..0000000 --- a/src/main/java/freemarker/ext/dom/DocumentModel.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package freemarker.ext.dom; - -import org.w3c.dom.Document; -import org.w3c.dom.NodeList; - -import freemarker.core.Environment; -import freemarker.template.TemplateHashModel; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; - -/** - * A class that wraps the root node of a parsed XML document, using - * the W3C DOM API. - */ - -class DocumentModel extends NodeModel implements TemplateHashModel { - - private ElementModel rootElement; - - DocumentModel(Document doc) { - super(doc); - } - - public String getNodeName() { - return "@document"; - } - - @Override - public TemplateModel get(String key) throws TemplateModelException { - if (key.equals("*")) { - return getRootElement(); - } else if (key.equals("**")) { - NodeList nl = ((Document) node).getElementsByTagName("*"); - return new NodeListModel(nl, this); - } else if (DomStringUtil.isXMLNameLike(key)) { - ElementModel em = (ElementModel) NodeModel.wrap(((Document) node).getDocumentElement()); - if (em.matchesName(key, Environment.getCurrentEnvironment())) { - return em; - } else { - return new NodeListModel(this); - } - } - return super.get(key); - } - - ElementModel getRootElement() { - if (rootElement == null) { - rootElement = (ElementModel) wrap(((Document) node).getDocumentElement()); - } - return rootElement; - } - - public boolean isEmpty() { - return false; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/DocumentTypeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/DocumentTypeModel.java b/src/main/java/freemarker/ext/dom/DocumentTypeModel.java deleted file mode 100644 index 30fa382..0000000 --- a/src/main/java/freemarker/ext/dom/DocumentTypeModel.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package freemarker.ext.dom; - -import org.w3c.dom.DocumentType; -import org.w3c.dom.ProcessingInstruction; - -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; -import freemarker.template.TemplateSequenceModel; - -class DocumentTypeModel extends NodeModel { - - public DocumentTypeModel(DocumentType docType) { - super(docType); - } - - public String getAsString() { - return ((ProcessingInstruction) node).getData(); - } - - public TemplateSequenceModel getChildren() throws TemplateModelException { - throw new TemplateModelException("entering the child nodes of a DTD node is not currently supported"); - } - - @Override - public TemplateModel get(String key) throws TemplateModelException { - throw new TemplateModelException("accessing properties of a DTD is not currently supported"); - } - - public String getNodeName() { - return "@document_type$" + ((DocumentType) node).getNodeName(); - } - - public boolean isEmpty() { - return true; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/DomStringUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/DomStringUtil.java b/src/main/java/freemarker/ext/dom/DomStringUtil.java deleted file mode 100644 index b93192b..0000000 --- a/src/main/java/freemarker/ext/dom/DomStringUtil.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package freemarker.ext.dom; - -import freemarker.core.Environment; -import freemarker.template.Template; - -/** - * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! - * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can - * access things inside this package that users shouldn't. - */ -final class DomStringUtil { - - private DomStringUtil() { - // Not meant to be instantiated - } - - static boolean isXMLNameLike(String name) { - return isXMLNameLike(name, 0); - } - - /** - * Check if the name looks like an XML element name. - * - * @param firstCharIdx The index of the character in the string parameter that we treat as the beginning of the - * string to check. This is to spare substringing that has become more expensive in Java 7. - * - * @return whether the name is a valid XML element name. (This routine might only be 99% accurate. REVISIT) - */ - static boolean isXMLNameLike(String name, int firstCharIdx) { - int ln = name.length(); - for (int i = firstCharIdx; i < ln; i++) { - char c = name.charAt(i); - if (i == firstCharIdx && (c == '-' || c == '.' || Character.isDigit(c))) { - return false; - } - if (!Character.isLetterOrDigit(c) && c != '_' && c != '-' && c != '.') { - if (c == ':') { - if (i + 1 < ln && name.charAt(i + 1) == ':') { - // "::" is used in XPath - return false; - } - // We don't return here, as a lonely ":" is allowed. - } else { - return false; - } - } - } - return true; - } - - /** - * @return whether the qname matches the combination of nodeName, nsURI, and environment prefix settings. - */ - static boolean matchesName(String qname, String nodeName, String nsURI, Environment env) { - String defaultNS = env.getDefaultNS(); - if ((defaultNS != null) && defaultNS.equals(nsURI)) { - return qname.equals(nodeName) - || qname.equals(Template.DEFAULT_NAMESPACE_PREFIX + ":" + nodeName); - } - if ("".equals(nsURI)) { - if (defaultNS != null) { - return qname.equals(Template.NO_NS_PREFIX + ":" + nodeName); - } else { - return qname.equals(nodeName) || qname.equals(Template.NO_NS_PREFIX + ":" + nodeName); - } - } - String prefix = env.getPrefixForNamespace(nsURI); - if (prefix == null) { - return false; // Is this the right thing here??? - } - return qname.equals(prefix + ":" + nodeName); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/ElementModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/ElementModel.java b/src/main/java/freemarker/ext/dom/ElementModel.java deleted file mode 100644 index 1d0c2b5..0000000 --- a/src/main/java/freemarker/ext/dom/ElementModel.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package freemarker.ext.dom; - -import java.util.Collections; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import freemarker.core.Environment; -import freemarker.template.SimpleScalar; -import freemarker.template.Template; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; -import freemarker.template.TemplateScalarModel; -import freemarker.template.TemplateSequenceModel; - -class ElementModel extends NodeModel implements TemplateScalarModel { - - public ElementModel(Element element) { - super(element); - } - - public boolean isEmpty() { - return false; - } - - /** - * An Element node supports various hash keys. - * Any key that corresponds to the tag name of any child elements - * returns a sequence of those elements. The special key "*" returns - * all the element's direct children. - * The "**" key return all the element's descendants in the order they - * occur in the document. - * Any key starting with '@' is taken to be the name of an element attribute. - * The special key "@@" returns a hash of all the element's attributes. - * The special key "/" returns the root document node associated with this element. - */ - @Override - public TemplateModel get(String key) throws TemplateModelException { - if (key.equals("*")) { - NodeListModel ns = new NodeListModel(this); - TemplateSequenceModel children = getChildNodes(); - for (int i = 0; i < children.size(); i++) { - NodeModel child = (NodeModel) children.get(i); - if (child.node.getNodeType() == Node.ELEMENT_NODE) { - ns.add(child); - } - } - return ns; - } else if (key.equals("**")) { - return new NodeListModel(((Element) node).getElementsByTagName("*"), this); - } else if (key.startsWith("@")) { - if (key.startsWith("@@")) { - if (key.equals(AtAtKey.ATTRIBUTES.getKey())) { - return new NodeListModel(node.getAttributes(), this); - } else if (key.equals(AtAtKey.START_TAG.getKey())) { - NodeOutputter nodeOutputter = new NodeOutputter(node); - return new SimpleScalar(nodeOutputter.getOpeningTag((Element) node)); - } else if (key.equals(AtAtKey.END_TAG.getKey())) { - NodeOutputter nodeOutputter = new NodeOutputter(node); - return new SimpleScalar(nodeOutputter.getClosingTag((Element) node)); - } else if (key.equals(AtAtKey.ATTRIBUTES_MARKUP.getKey())) { - StringBuilder buf = new StringBuilder(); - NodeOutputter nu = new NodeOutputter(node); - nu.outputContent(node.getAttributes(), buf); - return new SimpleScalar(buf.toString().trim()); - } else if (key.equals(AtAtKey.PREVIOUS_SIBLING_ELEMENT.getKey())) { - Node previousSibling = node.getPreviousSibling(); - while (previousSibling != null && !this.isSignificantNode(previousSibling)) { - previousSibling = previousSibling.getPreviousSibling(); - } - return previousSibling != null && previousSibling.getNodeType() == Node.ELEMENT_NODE - ? wrap(previousSibling) : new NodeListModel(Collections.emptyList(), null); - } else if (key.equals(AtAtKey.NEXT_SIBLING_ELEMENT.getKey())) { - Node nextSibling = node.getNextSibling(); - while (nextSibling != null && !this.isSignificantNode(nextSibling)) { - nextSibling = nextSibling.getNextSibling(); - } - return nextSibling != null && nextSibling.getNodeType() == Node.ELEMENT_NODE - ? wrap(nextSibling) : new NodeListModel(Collections.emptyList(), null); - } else { - // We don't know anything like this that's element-specific; fall back - return super.get(key); - } - } else { // Starts with "@", but not with "@@" - if (DomStringUtil.isXMLNameLike(key, 1)) { - Attr att = getAttribute(key.substring(1)); - if (att == null) { - return new NodeListModel(this); - } - return wrap(att); - } else if (key.equals("@*")) { - return new NodeListModel(node.getAttributes(), this); - } else { - // We don't know anything like this that's element-specific; fall back - return super.get(key); - } - } - } else if (DomStringUtil.isXMLNameLike(key)) { - // We interpret key as an element name - NodeListModel result = ((NodeListModel) getChildNodes()).filterByName(key); - return result.size() != 1 ? result : result.get(0); - } else { - // We don't anything like this that's element-specific; fall back - return super.get(key); - } - } - - public String getAsString() throws TemplateModelException { - NodeList nl = node.getChildNodes(); - String result = ""; - for (int i = 0; i < nl.getLength(); i++) { - Node child = nl.item(i); - int nodeType = child.getNodeType(); - if (nodeType == Node.ELEMENT_NODE) { - String msg = "Only elements with no child elements can be processed as text." - + "\nThis element with name \"" - + node.getNodeName() - + "\" has a child element named: " + child.getNodeName(); - throw new TemplateModelException(msg); - } else if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) { - result += child.getNodeValue(); - } - } - return result; - } - - public String getNodeName() { - String result = node.getLocalName(); - if (result == null || result.equals("")) { - result = node.getNodeName(); - } - return result; - } - - @Override - String getQualifiedName() { - String nodeName = getNodeName(); - String nsURI = getNodeNamespace(); - if (nsURI == null || nsURI.length() == 0) { - return nodeName; - } - Environment env = Environment.getCurrentEnvironment(); - String defaultNS = env.getDefaultNS(); - String prefix; - if (defaultNS != null && defaultNS.equals(nsURI)) { - prefix = ""; - } else { - prefix = env.getPrefixForNamespace(nsURI); - - } - if (prefix == null) { - return null; // We have no qualified name, because there is no prefix mapping - } - if (prefix.length() > 0) { - prefix += ":"; - } - return prefix + nodeName; - } - - private Attr getAttribute(String qname) { - Element element = (Element) node; - Attr result = element.getAttributeNode(qname); - if (result != null) - return result; - int colonIndex = qname.indexOf(':'); - if (colonIndex > 0) { - String prefix = qname.substring(0, colonIndex); - String uri; - if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) { - uri = Environment.getCurrentEnvironment().getDefaultNS(); - } else { - uri = Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix); - } - String localName = qname.substring(1 + colonIndex); - if (uri != null) { - result = element.getAttributeNodeNS(uri, localName); - } - } - return result; - } - - private boolean isSignificantNode(Node node) throws TemplateModelException { - return (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) - ? !isBlankXMLText(node.getTextContent()) - : node.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE && node.getNodeType() != Node.COMMENT_NODE; - } - - private boolean isBlankXMLText(String s) { - if (s == null) { - return true; - } - for (int i = 0; i < s.length(); i++) { - if (!isXMLWhiteSpace(s.charAt(i))) { - return false; - } - } - return true; - } - - /** - * White space according the XML spec. - */ - private boolean isXMLWhiteSpace(char c) { - return c == ' ' || c == '\t' || c == '\n' | c == '\r'; - } - - boolean matchesName(String name, Environment env) { - return DomStringUtil.matchesName(name, getNodeName(), getNodeNamespace(), env); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/JaxenXPathSupport.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/JaxenXPathSupport.java b/src/main/java/freemarker/ext/dom/JaxenXPathSupport.java deleted file mode 100644 index 109e82b..0000000 --- a/src/main/java/freemarker/ext/dom/JaxenXPathSupport.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package freemarker.ext.dom; - -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.jaxen.BaseXPath; -import org.jaxen.Function; -import org.jaxen.FunctionCallException; -import org.jaxen.FunctionContext; -import org.jaxen.JaxenException; -import org.jaxen.NamespaceContext; -import org.jaxen.Navigator; -import org.jaxen.UnresolvableException; -import org.jaxen.VariableContext; -import org.jaxen.XPathFunctionContext; -import org.jaxen.dom.DocumentNavigator; -import org.w3c.dom.Document; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import freemarker.core.CustomAttribute; -import freemarker.core.Environment; -import freemarker.template.ObjectWrapper; -import freemarker.template.Template; -import freemarker.template.TemplateBooleanModel; -import freemarker.template.TemplateDateModel; -import freemarker.template.TemplateException; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; -import freemarker.template.TemplateNumberModel; -import freemarker.template.TemplateScalarModel; -import freemarker.template.utility.UndeclaredThrowableException; - - -/** - */ -class JaxenXPathSupport implements XPathSupport { - - private static final CustomAttribute XPATH_CACHE_ATTR = - new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE) { - @Override - protected Object create() { - return new HashMap<String, BaseXPath>(); - } - }; - - // [2.4] Can't we just use Collections.emptyList()? - private final static ArrayList EMPTY_ARRAYLIST = new ArrayList(); - - public TemplateModel executeQuery(Object context, String xpathQuery) throws TemplateModelException { - try { - BaseXPath xpath; - Map<String, BaseXPath> xpathCache = (Map<String, BaseXPath>) XPATH_CACHE_ATTR.get(); - synchronized (xpathCache) { - xpath = xpathCache.get(xpathQuery); - if (xpath == null) { - xpath = new BaseXPath(xpathQuery, FM_DOM_NAVIGATOR); - xpath.setNamespaceContext(customNamespaceContext); - xpath.setFunctionContext(FM_FUNCTION_CONTEXT); - xpath.setVariableContext(FM_VARIABLE_CONTEXT); - xpathCache.put(xpathQuery, xpath); - } - } - List result = xpath.selectNodes(context != null ? context : EMPTY_ARRAYLIST); - if (result.size() == 1) { - // [2.4] Use the proper object wrapper (argument in 2.4) - return ObjectWrapper.DEFAULT_WRAPPER.wrap(result.get(0)); - } - NodeListModel nlm = new NodeListModel(result, null); - nlm.xpathSupport = this; - return nlm; - } catch (UndeclaredThrowableException e) { - Throwable t = e.getUndeclaredThrowable(); - if (t instanceof TemplateModelException) { - throw (TemplateModelException) t; - } - throw e; - } catch (JaxenException je) { - throw new TemplateModelException(je); - } - } - - static private final NamespaceContext customNamespaceContext = new NamespaceContext() { - - public String translateNamespacePrefixToUri(String prefix) { - if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) { - return Environment.getCurrentEnvironment().getDefaultNS(); - } - return Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix); - } - }; - - private static final VariableContext FM_VARIABLE_CONTEXT = new VariableContext() { - public Object getVariableValue(String namespaceURI, String prefix, String localName) - throws UnresolvableException { - try { - TemplateModel model = Environment.getCurrentEnvironment().getVariable(localName); - if (model == null) { - throw new UnresolvableException("Variable \"" + localName + "\" not found."); - } - if (model instanceof TemplateScalarModel) { - return ((TemplateScalarModel) model).getAsString(); - } - if (model instanceof TemplateNumberModel) { - return ((TemplateNumberModel) model).getAsNumber(); - } - if (model instanceof TemplateDateModel) { - return ((TemplateDateModel) model).getAsDate(); - } - if (model instanceof TemplateBooleanModel) { - return Boolean.valueOf(((TemplateBooleanModel) model).getAsBoolean()); - } - } catch (TemplateModelException e) { - throw new UndeclaredThrowableException(e); - } - throw new UnresolvableException( - "Variable \"" + localName + "\" exists, but it's not a string, number, date, or boolean"); - } - }; - - private static final FunctionContext FM_FUNCTION_CONTEXT = new XPathFunctionContext() { - @Override - public Function getFunction(String namespaceURI, String prefix, String localName) - throws UnresolvableException { - try { - return super.getFunction(namespaceURI, prefix, localName); - } catch (UnresolvableException e) { - return super.getFunction(null, null, localName); - } - } - }; - - /** - * Stores the the template parsed as {@link Document} in the template itself. - */ - private static final CustomAttribute FM_DOM_NAVIAGOTOR_CACHED_DOM - = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); - - private static final Navigator FM_DOM_NAVIGATOR = new DocumentNavigator() { - @Override - public Object getDocument(String uri) throws FunctionCallException { - try { - Template raw = getTemplate(uri); - Document doc = (Document) FM_DOM_NAVIAGOTOR_CACHED_DOM.get(raw); - if (doc == null) { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - DocumentBuilder builder = factory.newDocumentBuilder(); - FmEntityResolver er = new FmEntityResolver(); - builder.setEntityResolver(er); - doc = builder.parse(createInputSource(null, raw)); - // If the entity resolver got called 0 times, the document - // is standalone, so we can safely cache it - if (er.getCallCount() == 0) { - FM_DOM_NAVIAGOTOR_CACHED_DOM.set(doc, raw); - } - } - return doc; - } catch (Exception e) { - throw new FunctionCallException("Failed to parse document for URI: " + uri, e); - } - } - }; - - static Template getTemplate(String systemId) throws IOException { - Environment env = Environment.getCurrentEnvironment(); - String encoding = env.getTemplate().getEncoding(); - if (encoding == null) { - encoding = env.getConfiguration().getEncoding(env.getLocale()); - } - String templatePath = env.getTemplate().getName(); - int lastSlash = templatePath.lastIndexOf('/'); - templatePath = lastSlash == -1 ? "" : templatePath.substring(0, lastSlash + 1); - systemId = env.toFullTemplateName(templatePath, systemId); - Template raw = env.getConfiguration().getTemplate(systemId, env.getLocale(), encoding, false); - return raw; - } - - private static InputSource createInputSource(String publicId, Template raw) throws IOException, SAXException { - StringWriter sw = new StringWriter(); - try { - raw.process(Collections.EMPTY_MAP, sw); - } catch (TemplateException e) { - throw new SAXException(e); - } - InputSource is = new InputSource(); - is.setPublicId(publicId); - is.setSystemId(raw.getName()); - is.setCharacterStream(new StringReader(sw.toString())); - return is; - } - - private static class FmEntityResolver implements EntityResolver { - private int callCount = 0; - - public InputSource resolveEntity(String publicId, String systemId) - throws SAXException, IOException { - ++callCount; - return createInputSource(publicId, getTemplate(systemId)); - } - - int getCallCount() { - return callCount; - } - }; -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/NodeListModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/NodeListModel.java b/src/main/java/freemarker/ext/dom/NodeListModel.java deleted file mode 100644 index 676e2ec..0000000 --- a/src/main/java/freemarker/ext/dom/NodeListModel.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package freemarker.ext.dom; - -import java.util.ArrayList; -import java.util.List; - -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import freemarker.core.Environment; -import freemarker.core._UnexpectedTypeErrorExplainerTemplateModel; -import freemarker.template.Configuration; -import freemarker.template.ObjectWrapper; -import freemarker.template.SimpleScalar; -import freemarker.template.SimpleSequence; -import freemarker.template.TemplateBooleanModel; -import freemarker.template.TemplateDateModel; -import freemarker.template.TemplateHashModel; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; -import freemarker.template.TemplateNodeModel; -import freemarker.template.TemplateNumberModel; -import freemarker.template.TemplateScalarModel; -import freemarker.template.TemplateSequenceModel; - -/** - * Used when the result set contains 0 or multiple nodes; shouldn't be used when you have exactly 1 node. For exactly 1 - * node, use {@link NodeModel#wrap(Node)}, because {@link NodeModel} subclasses can have extra features building on that - * restriction, like single elements with text content can be used as FTL string-s. - * - * <p> - * This class is not guaranteed to be thread safe, so instances of this shouldn't be used as shared variable ( - * {@link Configuration#setSharedVariable(String, Object)}). - */ -class NodeListModel extends SimpleSequence implements TemplateHashModel, _UnexpectedTypeErrorExplainerTemplateModel { - - // [2.4] make these private - NodeModel contextNode; - XPathSupport xpathSupport; - - private static ObjectWrapper nodeWrapper = new ObjectWrapper() { - @Override - public TemplateModel wrap(Object obj) { - if (obj instanceof NodeModel) { - return (NodeModel) obj; - } - return NodeModel.wrap((Node) obj); - } - }; - - NodeListModel(Node contextNode) { - this(NodeModel.wrap(contextNode)); - } - - NodeListModel(NodeModel contextNode) { - super(nodeWrapper); - this.contextNode = contextNode; - } - - NodeListModel(NodeList nodeList, NodeModel contextNode) { - super(nodeWrapper); - for (int i = 0; i < nodeList.getLength(); i++) { - list.add(nodeList.item(i)); - } - this.contextNode = contextNode; - } - - NodeListModel(NamedNodeMap nodeList, NodeModel contextNode) { - super(nodeWrapper); - for (int i = 0; i < nodeList.getLength(); i++) { - list.add(nodeList.item(i)); - } - this.contextNode = contextNode; - } - - NodeListModel(List list, NodeModel contextNode) { - super(list, nodeWrapper); - this.contextNode = contextNode; - } - - NodeListModel filterByName(String name) throws TemplateModelException { - NodeListModel result = new NodeListModel(contextNode); - int size = size(); - if (size == 0) { - return result; - } - Environment env = Environment.getCurrentEnvironment(); - for (int i = 0; i < size; i++) { - NodeModel nm = (NodeModel) get(i); - if (nm instanceof ElementModel) { - if (((ElementModel) nm).matchesName(name, env)) { - result.add(nm); - } - } - } - return result; - } - - @Override - public boolean isEmpty() { - return size() == 0; - } - - @Override - public TemplateModel get(String key) throws TemplateModelException { - if (size() == 1) { - NodeModel nm = (NodeModel) get(0); - return nm.get(key); - } - if (key.startsWith("@@")) { - if (key.equals(AtAtKey.MARKUP.getKey()) - || key.equals(AtAtKey.NESTED_MARKUP.getKey()) - || key.equals(AtAtKey.TEXT.getKey())) { - StringBuilder result = new StringBuilder(); - for (int i = 0; i < size(); i++) { - NodeModel nm = (NodeModel) get(i); - TemplateScalarModel textModel = (TemplateScalarModel) nm.get(key); - result.append(textModel.getAsString()); - } - return new SimpleScalar(result.toString()); - } else if (key.length() != 2 /* to allow "@@" to fall through */) { - // As @@... would cause exception in the XPath engine, we throw a nicer exception now. - if (AtAtKey.containsKey(key)) { - throw new TemplateModelException( - "\"" + key + "\" is only applicable to a single XML node, but it was applied on " - + (size() != 0 - ? size() + " XML nodes (multiple matches)." - : "an empty list of XML nodes (no matches).")); - } else { - throw new TemplateModelException("Unsupported @@ key: " + key); - } - } - } - if (DomStringUtil.isXMLNameLike(key) - || ((key.startsWith("@") - && (DomStringUtil.isXMLNameLike(key, 1) || key.equals("@@") || key.equals("@*")))) - || key.equals("*") || key.equals("**")) { - NodeListModel result = new NodeListModel(contextNode); - for (int i = 0; i < size(); i++) { - NodeModel nm = (NodeModel) get(i); - if (nm instanceof ElementModel) { - TemplateSequenceModel tsm = (TemplateSequenceModel) ((ElementModel) nm).get(key); - if (tsm != null) { - int size = tsm.size(); - for (int j = 0; j < size; j++) { - result.add(tsm.get(j)); - } - } - } - } - if (result.size() == 1) { - return result.get(0); - } - return result; - } - XPathSupport xps = getXPathSupport(); - if (xps != null) { - Object context = (size() == 0) ? null : rawNodeList(); - return xps.executeQuery(context, key); - } else { - throw new TemplateModelException( - "Can't try to resolve the XML query key, because no XPath support is available. " - + "This is either malformed or an XPath expression: " + key); - } - } - - private List rawNodeList() throws TemplateModelException { - int size = size(); - ArrayList al = new ArrayList(size); - for (int i = 0; i < size; i++) { - al.add(((NodeModel) get(i)).node); - } - return al; - } - - XPathSupport getXPathSupport() throws TemplateModelException { - if (xpathSupport == null) { - if (contextNode != null) { - xpathSupport = contextNode.getXPathSupport(); - } else if (size() > 0) { - xpathSupport = ((NodeModel) get(0)).getXPathSupport(); - } - } - return xpathSupport; - } - - @Override - public Object[] explainTypeError(Class[] expectedClasses) { - for (int i = 0; i < expectedClasses.length; i++) { - Class expectedClass = expectedClasses[i]; - if (TemplateScalarModel.class.isAssignableFrom(expectedClass) - || TemplateDateModel.class.isAssignableFrom(expectedClass) - || TemplateNumberModel.class.isAssignableFrom(expectedClass) - || TemplateBooleanModel.class.isAssignableFrom(expectedClass)) { - return newTypeErrorExplanation("string"); - } else if (TemplateNodeModel.class.isAssignableFrom(expectedClass)) { - return newTypeErrorExplanation("node"); - } - } - return null; - } - - private Object[] newTypeErrorExplanation(String type) { - return new Object[] { - "This XML query result can't be used as ", type, " because for that it had to contain exactly " - + "1 XML node, but it contains ", Integer.valueOf(size()), " nodes. " - + "That is, the constructing XML query has found ", - isEmpty() - ? "no matches." - : "multiple matches." - }; - } - -} \ No newline at end of file
