mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-02 17:35:18 +00:00
- Move ISO9705 to util git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2020 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
705 lines
25 KiB
Java
705 lines
25 KiB
Java
/*
|
|
* Copyright (C) 2005 Alfresco, Inc.
|
|
*
|
|
* Licensed under the Mozilla Public License version 1.1
|
|
* with a permitted attribution clause. You may obtain a
|
|
* copy of the License at
|
|
*
|
|
* http://www.alfresco.org/legal/license.txt
|
|
*
|
|
* 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 org.alfresco.repo.search;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.StringTokenizer;
|
|
|
|
import org.alfresco.error.AlfrescoRuntimeException;
|
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.search.QueryParameterDefinition;
|
|
import org.alfresco.service.cmr.search.SearchParameters;
|
|
import org.alfresco.service.namespace.NamespacePrefixResolver;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.service.namespace.QNamePattern;
|
|
import org.alfresco.util.ISO9075;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.jaxen.BaseXPath;
|
|
import org.jaxen.Context;
|
|
import org.jaxen.Function;
|
|
import org.jaxen.FunctionCallException;
|
|
import org.jaxen.FunctionContext;
|
|
import org.jaxen.JaxenException;
|
|
import org.jaxen.Navigator;
|
|
import org.jaxen.SimpleFunctionContext;
|
|
import org.jaxen.SimpleVariableContext;
|
|
import org.jaxen.function.BooleanFunction;
|
|
import org.jaxen.function.CeilingFunction;
|
|
import org.jaxen.function.ConcatFunction;
|
|
import org.jaxen.function.ContainsFunction;
|
|
import org.jaxen.function.CountFunction;
|
|
import org.jaxen.function.FalseFunction;
|
|
import org.jaxen.function.FloorFunction;
|
|
import org.jaxen.function.IdFunction;
|
|
import org.jaxen.function.LangFunction;
|
|
import org.jaxen.function.LastFunction;
|
|
import org.jaxen.function.LocalNameFunction;
|
|
import org.jaxen.function.NameFunction;
|
|
import org.jaxen.function.NamespaceUriFunction;
|
|
import org.jaxen.function.NormalizeSpaceFunction;
|
|
import org.jaxen.function.NotFunction;
|
|
import org.jaxen.function.NumberFunction;
|
|
import org.jaxen.function.PositionFunction;
|
|
import org.jaxen.function.RoundFunction;
|
|
import org.jaxen.function.StartsWithFunction;
|
|
import org.jaxen.function.StringFunction;
|
|
import org.jaxen.function.StringLengthFunction;
|
|
import org.jaxen.function.SubstringAfterFunction;
|
|
import org.jaxen.function.SubstringBeforeFunction;
|
|
import org.jaxen.function.SubstringFunction;
|
|
import org.jaxen.function.SumFunction;
|
|
import org.jaxen.function.TranslateFunction;
|
|
import org.jaxen.function.TrueFunction;
|
|
import org.jaxen.function.ext.EndsWithFunction;
|
|
import org.jaxen.function.ext.EvaluateFunction;
|
|
import org.jaxen.function.ext.LowerFunction;
|
|
import org.jaxen.function.ext.MatrixConcatFunction;
|
|
import org.jaxen.function.ext.UpperFunction;
|
|
import org.jaxen.function.xslt.DocumentFunction;
|
|
|
|
/**
|
|
* Represents an xpath statement that resolves against a
|
|
* <code>NodeService</code>
|
|
*
|
|
* @author Andy Hind
|
|
*/
|
|
public class NodeServiceXPath extends BaseXPath
|
|
{
|
|
private static final long serialVersionUID = 3834032441789592882L;
|
|
|
|
private static String JCR_URI = "http://www.jcp.org/jcr/1.0";
|
|
|
|
private static Log logger = LogFactory.getLog(NodeServiceXPath.class);
|
|
|
|
/**
|
|
*
|
|
* @param xpath
|
|
* the xpath statement
|
|
* @param documentNavigator
|
|
* the navigator that will allow the xpath to be resolved
|
|
* @param paramDefs
|
|
* parameters to resolve variables required by xpath
|
|
* @throws JaxenException
|
|
*/
|
|
public NodeServiceXPath(String xpath, DocumentNavigator documentNavigator, QueryParameterDefinition[] paramDefs)
|
|
throws JaxenException
|
|
{
|
|
super(xpath, documentNavigator);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("Created XPath: \n")
|
|
.append(" XPath: ").append(xpath).append("\n")
|
|
.append(" Parameters: \n");
|
|
for (int i = 0; paramDefs != null && i < paramDefs.length; i++)
|
|
{
|
|
sb.append(" Parameter: \n")
|
|
.append(" name: ").append(paramDefs[i].getQName()).append("\n")
|
|
.append(" value: ").append(paramDefs[i].getDefault()).append("\n");
|
|
}
|
|
logger.debug(sb.toString());
|
|
}
|
|
|
|
// Add support for parameters
|
|
if (paramDefs != null)
|
|
{
|
|
SimpleVariableContext svc = (SimpleVariableContext) this.getVariableContext();
|
|
for (int i = 0; i < paramDefs.length; i++)
|
|
{
|
|
if (!paramDefs[i].hasDefaultValue())
|
|
{
|
|
throw new AlfrescoRuntimeException("Parameter must have default value");
|
|
}
|
|
Object value = null;
|
|
if (paramDefs[i].getDataTypeDefinition().getName().equals(DataTypeDefinition.BOOLEAN))
|
|
{
|
|
value = Boolean.valueOf(paramDefs[i].getDefault());
|
|
}
|
|
else if (paramDefs[i].getDataTypeDefinition().getName().equals(DataTypeDefinition.DOUBLE))
|
|
{
|
|
value = Double.valueOf(paramDefs[i].getDefault());
|
|
}
|
|
else if (paramDefs[i].getDataTypeDefinition().getName().equals(DataTypeDefinition.FLOAT))
|
|
{
|
|
value = Float.valueOf(paramDefs[i].getDefault());
|
|
}
|
|
else if (paramDefs[i].getDataTypeDefinition().getName().equals(DataTypeDefinition.INT))
|
|
{
|
|
value = Integer.valueOf(paramDefs[i].getDefault());
|
|
}
|
|
else if (paramDefs[i].getDataTypeDefinition().getName().equals(DataTypeDefinition.LONG))
|
|
{
|
|
value = Long.valueOf(paramDefs[i].getDefault());
|
|
}
|
|
else
|
|
{
|
|
value = paramDefs[i].getDefault();
|
|
}
|
|
svc.setVariableValue(paramDefs[i].getQName().getNamespaceURI(), paramDefs[i].getQName().getLocalName(),
|
|
value);
|
|
}
|
|
}
|
|
|
|
for (String prefix : documentNavigator.getNamespacePrefixResolver().getPrefixes())
|
|
{
|
|
addNamespace(prefix, documentNavigator.getNamespacePrefixResolver().getNamespaceURI(prefix));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Jaxen has some magic with its IdentitySet, which means that we can get different results
|
|
* depending on whether we cache {@link ChildAssociationRef } instances or not.
|
|
* <p>
|
|
* So, duplicates are eliminated here before the results are returned.
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
@Override
|
|
public List selectNodes(Object arg0) throws JaxenException
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Selecting using XPath: \n" +
|
|
" XPath: " + this + "\n" +
|
|
" starting at: " + arg0);
|
|
}
|
|
|
|
List<Object> resultsWithDuplicates = super.selectNodes(arg0);
|
|
|
|
Set<Object> set = new HashSet<Object>(resultsWithDuplicates);
|
|
|
|
// now return as a list again
|
|
List<Object> results = resultsWithDuplicates;
|
|
results.clear();
|
|
results.addAll(set);
|
|
|
|
// done
|
|
return results;
|
|
}
|
|
|
|
public static class FirstFunction implements Function
|
|
{
|
|
|
|
public Object call(Context context, List args) throws FunctionCallException
|
|
{
|
|
if (args.size() == 0)
|
|
{
|
|
return evaluate(context);
|
|
}
|
|
|
|
throw new FunctionCallException("first() requires no arguments.");
|
|
}
|
|
|
|
public static Double evaluate(Context context)
|
|
{
|
|
return new Double(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A boolean function to determine if a node type is a subtype of another
|
|
* type
|
|
*/
|
|
static class SubTypeOf implements Function
|
|
{
|
|
public Object call(Context context, List args) throws FunctionCallException
|
|
{
|
|
if (args.size() != 1)
|
|
{
|
|
throw new FunctionCallException("subtypeOf() requires one argument: subtypeOf(QName typeQName)");
|
|
}
|
|
return evaluate(context.getNodeSet(), args.get(0), context.getNavigator());
|
|
}
|
|
|
|
public Object evaluate(List nodes, Object qnameObj, Navigator nav)
|
|
{
|
|
if (nodes.size() != 1)
|
|
{
|
|
return false;
|
|
}
|
|
// resolve the qname of the type we are checking for
|
|
String qnameStr = StringFunction.evaluate(qnameObj, nav);
|
|
if (qnameStr.equals("*"))
|
|
{
|
|
return true;
|
|
}
|
|
QName typeQName;
|
|
|
|
if (qnameStr.startsWith("{"))
|
|
{
|
|
typeQName = QName.createQName(qnameStr);
|
|
}
|
|
else
|
|
{
|
|
typeQName = QName.createQName(qnameStr, ((DocumentNavigator) nav).getNamespacePrefixResolver());
|
|
}
|
|
// resolve the noderef
|
|
NodeRef nodeRef = null;
|
|
if (nav.isElement(nodes.get(0)))
|
|
{
|
|
nodeRef = ((ChildAssociationRef) nodes.get(0)).getChildRef();
|
|
}
|
|
else if (nav.isAttribute(nodes.get(0)))
|
|
{
|
|
nodeRef = ((DocumentNavigator.Property) nodes.get(0)).parent;
|
|
}
|
|
|
|
DocumentNavigator dNav = (DocumentNavigator) nav;
|
|
boolean result = dNav.isSubtypeOf(nodeRef, typeQName);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
static class Deref implements Function
|
|
{
|
|
|
|
public Object call(Context context, List args) throws FunctionCallException
|
|
{
|
|
if (args.size() == 2)
|
|
{
|
|
return evaluate(args.get(0), args.get(1), context.getNavigator());
|
|
}
|
|
|
|
throw new FunctionCallException("deref() requires two arguments.");
|
|
}
|
|
|
|
public Object evaluate(Object attributeName, Object pattern, Navigator nav)
|
|
{
|
|
List<Object> answer = new ArrayList<Object>();
|
|
String attributeValue = StringFunction.evaluate(attributeName, nav);
|
|
String patternValue = StringFunction.evaluate(pattern, nav);
|
|
|
|
// TODO: Ignore the pattern for now
|
|
// Should do a type pattern test
|
|
if ((attributeValue != null) && (attributeValue.length() > 0))
|
|
{
|
|
DocumentNavigator dNav = (DocumentNavigator) nav;
|
|
NodeRef nodeRef = new NodeRef(attributeValue);
|
|
if (patternValue.equals("*"))
|
|
{
|
|
answer.add(dNav.getNode(nodeRef));
|
|
}
|
|
else
|
|
{
|
|
QNamePattern qNamePattern = new JCRPatternMatch(patternValue, dNav.getNamespacePrefixResolver());
|
|
answer.addAll(dNav.getNode(nodeRef, qNamePattern));
|
|
}
|
|
|
|
}
|
|
return answer;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A boolean function to determine if a node property matches a pattern
|
|
* and/or the node text matches the pattern.
|
|
* <p>
|
|
* The default is JSR170 compliant. The optional boolean allows searching
|
|
* only against the property value itself.
|
|
* <p>
|
|
* The search is always case-insensitive.
|
|
*
|
|
* @author Derek Hulley
|
|
*/
|
|
static class Like implements Function
|
|
{
|
|
public Object call(Context context, List args) throws FunctionCallException
|
|
{
|
|
if (args.size() < 2 || args.size() > 3)
|
|
{
|
|
throw new FunctionCallException("like() usage: like(@attr, 'pattern' [, includeFTS]) \n"
|
|
+ " - includeFTS can be 'true' or 'false' \n"
|
|
+ " - search is case-insensitive");
|
|
}
|
|
// default includeFTS to true
|
|
return evaluate(context.getNodeSet(), args.get(0), args.get(1), args.size() == 2 ? Boolean.toString(true)
|
|
: args.get(2), context.getNavigator());
|
|
}
|
|
|
|
public Object evaluate(List nodes, Object obj, Object patternObj, Object includeFtsObj, Navigator nav)
|
|
{
|
|
Object attribute = null;
|
|
if (obj instanceof List)
|
|
{
|
|
List list = (List) obj;
|
|
if (list.isEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
// do not recurse: only first list should unwrap
|
|
attribute = list.get(0);
|
|
}
|
|
if ((attribute == null) || !nav.isAttribute(attribute))
|
|
{
|
|
return false;
|
|
}
|
|
if (nodes.size() != 1)
|
|
{
|
|
return false;
|
|
}
|
|
if (!nav.isElement(nodes.get(0)))
|
|
{
|
|
return false;
|
|
}
|
|
ChildAssociationRef car = (ChildAssociationRef) nodes.get(0);
|
|
String pattern = StringFunction.evaluate(patternObj, nav);
|
|
boolean includeFts = BooleanFunction.evaluate(includeFtsObj, nav);
|
|
QName qname = QName.createQName(nav.getAttributeNamespaceUri(attribute), ISO9075.decode(nav
|
|
.getAttributeName(attribute)));
|
|
|
|
DocumentNavigator dNav = (DocumentNavigator) nav;
|
|
// JSR 170 includes full text matches
|
|
return dNav.like(car.getChildRef(), qname, pattern, includeFts);
|
|
|
|
}
|
|
}
|
|
|
|
static class Contains implements Function
|
|
{
|
|
|
|
public Object call(Context context, List args) throws FunctionCallException
|
|
{
|
|
if (args.size() == 1)
|
|
{
|
|
return evaluate(context.getNodeSet(), args.get(0), context.getNavigator());
|
|
}
|
|
|
|
throw new FunctionCallException("contains() requires one argument.");
|
|
}
|
|
|
|
public Object evaluate(List nodes, Object pattern, Navigator nav)
|
|
{
|
|
if (nodes.size() != 1)
|
|
{
|
|
return false;
|
|
}
|
|
QName qname = null;
|
|
NodeRef nodeRef = null;
|
|
if (nav.isElement(nodes.get(0)))
|
|
{
|
|
qname = null; // should use all attributes and full text index
|
|
nodeRef = ((ChildAssociationRef) nodes.get(0)).getChildRef();
|
|
}
|
|
else if (nav.isAttribute(nodes.get(0)))
|
|
{
|
|
qname = QName.createQName(nav.getAttributeNamespaceUri(nodes.get(0)), ISO9075.decode(nav
|
|
.getAttributeName(nodes.get(0))));
|
|
nodeRef = ((DocumentNavigator.Property) nodes.get(0)).parent;
|
|
}
|
|
|
|
String patternValue = StringFunction.evaluate(pattern, nav);
|
|
DocumentNavigator dNav = (DocumentNavigator) nav;
|
|
|
|
return dNav.contains(nodeRef, qname, patternValue, SearchParameters.OR);
|
|
|
|
}
|
|
}
|
|
|
|
static class JCRContains implements Function
|
|
{
|
|
|
|
public Object call(Context context, List args) throws FunctionCallException
|
|
{
|
|
if (args.size() == 2)
|
|
{
|
|
if (context.getNavigator().isAttribute(context.getNodeSet().get(0)))
|
|
{
|
|
throw new FunctionCallException("jcr:contains() does not apply to an attribute context.");
|
|
}
|
|
return evaluate(context.getNodeSet(), args.get(0), args.get(1), context.getNavigator());
|
|
}
|
|
|
|
throw new FunctionCallException("contains() requires two argument.");
|
|
}
|
|
|
|
public Object evaluate(List nodes, Object identifier, Object pattern, Navigator nav)
|
|
{
|
|
if (nodes.size() != 1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QName qname = null;
|
|
NodeRef nodeRef = null;
|
|
|
|
Object target = identifier;
|
|
|
|
if (identifier instanceof List)
|
|
{
|
|
List list = (List) identifier;
|
|
if (list.isEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
// do not recurse: only first list should unwrap
|
|
target = list.get(0);
|
|
}
|
|
|
|
if (nav.isElement(target))
|
|
{
|
|
qname = null; // should use all attributes and full text index
|
|
nodeRef = ((ChildAssociationRef) target).getChildRef();
|
|
}
|
|
else if (nav.isAttribute(target))
|
|
{
|
|
qname = QName.createQName(nav.getAttributeNamespaceUri(target), ISO9075.decode(nav.getAttributeName(target)));
|
|
nodeRef = ((DocumentNavigator.Property) target).parent;
|
|
}
|
|
|
|
String patternValue = StringFunction.evaluate(pattern, nav);
|
|
DocumentNavigator dNav = (DocumentNavigator) nav;
|
|
|
|
return dNav.contains(nodeRef, qname, patternValue, SearchParameters.AND);
|
|
|
|
}
|
|
}
|
|
|
|
static class Score implements Function
|
|
{
|
|
private Double one = new Double(1);
|
|
|
|
public Object call(Context context, List args) throws FunctionCallException
|
|
{
|
|
return evaluate(context.getNodeSet(), context.getNavigator());
|
|
}
|
|
|
|
public Object evaluate(List nodes, Navigator nav)
|
|
{
|
|
return one;
|
|
|
|
}
|
|
}
|
|
|
|
protected FunctionContext createFunctionContext()
|
|
{
|
|
return XPathFunctionContext.getInstance();
|
|
}
|
|
|
|
public static class XPathFunctionContext extends SimpleFunctionContext
|
|
{
|
|
/**
|
|
* Singleton implementation.
|
|
*/
|
|
private static class Singleton
|
|
{
|
|
/**
|
|
* Singleton instance.
|
|
*/
|
|
private static XPathFunctionContext instance = new XPathFunctionContext();
|
|
}
|
|
|
|
/**
|
|
* Retrieve the singleton instance.
|
|
*
|
|
* @return the singleton instance
|
|
*/
|
|
public static FunctionContext getInstance()
|
|
{
|
|
return Singleton.instance;
|
|
}
|
|
|
|
/**
|
|
* Construct.
|
|
*
|
|
* <p>
|
|
* Construct with all core XPath functions registered.
|
|
* </p>
|
|
*/
|
|
public XPathFunctionContext()
|
|
{
|
|
// XXX could this be a HotSpot????
|
|
registerFunction("", // namespace URI
|
|
"boolean", new BooleanFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"ceiling", new CeilingFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"concat", new ConcatFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"contains", new ContainsFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"count", new CountFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"document", new DocumentFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"false", new FalseFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"floor", new FloorFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"id", new IdFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"lang", new LangFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"last", new LastFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"local-name", new LocalNameFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"name", new NameFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"namespace-uri", new NamespaceUriFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"normalize-space", new NormalizeSpaceFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"not", new NotFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"number", new NumberFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"position", new PositionFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"round", new RoundFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"starts-with", new StartsWithFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"string", new StringFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"string-length", new StringLengthFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"substring-after", new SubstringAfterFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"substring-before", new SubstringBeforeFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"substring", new SubstringFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"sum", new SumFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"true", new TrueFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"translate", new TranslateFunction());
|
|
|
|
// register extension functions
|
|
// extension functions should go into a namespace, but which one?
|
|
// for now, keep them in default namespace to not break any code
|
|
|
|
registerFunction("", // namespace URI
|
|
"matrix-concat", new MatrixConcatFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"evaluate", new EvaluateFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"lower-case", new LowerFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"upper-case", new UpperFunction());
|
|
|
|
registerFunction("", // namespace URI
|
|
"ends-with", new EndsWithFunction());
|
|
|
|
registerFunction("", "subtypeOf", new SubTypeOf());
|
|
registerFunction("", "deref", new Deref());
|
|
registerFunction("", "like", new Like());
|
|
registerFunction("", "contains", new Contains());
|
|
|
|
registerFunction("", "first", new FirstFunction());
|
|
|
|
// 170 functions
|
|
|
|
registerFunction(JCR_URI, "like", new Like());
|
|
registerFunction(JCR_URI, "score", new Score());
|
|
registerFunction(JCR_URI, "contains", new JCRContains());
|
|
registerFunction(JCR_URI, "deref", new Deref());
|
|
|
|
}
|
|
}
|
|
|
|
public static class JCRPatternMatch implements QNamePattern
|
|
{
|
|
private List<String> searches = new ArrayList<String>();
|
|
|
|
private NamespacePrefixResolver resolver;
|
|
|
|
/**
|
|
* Construct
|
|
*
|
|
* @param pattern
|
|
* JCR Pattern
|
|
* @param resolver
|
|
* Namespace Prefix Resolver
|
|
*/
|
|
public JCRPatternMatch(String pattern, NamespacePrefixResolver resolver)
|
|
{
|
|
// TODO: Check for valid pattern
|
|
|
|
// Convert to regular expression
|
|
String regexPattern = pattern.replaceAll("\\*", ".*");
|
|
|
|
// Split into independent search strings
|
|
StringTokenizer tokenizer = new StringTokenizer(regexPattern, "|", false);
|
|
while (tokenizer.hasMoreTokens())
|
|
{
|
|
String disjunct = tokenizer.nextToken().trim();
|
|
this.searches.add(disjunct);
|
|
}
|
|
|
|
this.resolver = resolver;
|
|
}
|
|
|
|
/*
|
|
* (non-Javadoc)
|
|
*
|
|
* @see org.alfresco.service.namespace.QNamePattern#isMatch(org.alfresco.service.namespace.QName)
|
|
*/
|
|
public boolean isMatch(QName qname)
|
|
{
|
|
String prefixedName = qname.toPrefixString(resolver);
|
|
for (String search : searches)
|
|
{
|
|
if (prefixedName.matches(search))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
}
|