You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2014/10/07 17:10:40 UTC
svn commit: r1629907 [2/3] - in /sling/trunk/contrib/validation: ./ api/
api/src/ api/src/main/ api/src/main/java/ api/src/main/java/org/
api/src/main/java/org/apache/ api/src/main/java/org/apache/sling/
api/src/main/java/org/apache/sling/validation/ a...
Added: sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/ValidationServiceImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/ValidationServiceImpl.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/ValidationServiceImpl.java (added)
+++ sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/ValidationServiceImpl.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,390 @@
+/*
+ * 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 org.apache.sling.validation.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.commons.threads.ThreadPool;
+import org.apache.sling.commons.threads.ThreadPoolManager;
+import org.apache.sling.validation.api.ChildResource;
+import org.apache.sling.validation.api.ResourceProperty;
+import org.apache.sling.validation.api.Type;
+import org.apache.sling.validation.api.ValidationModel;
+import org.apache.sling.validation.api.ValidationResult;
+import org.apache.sling.validation.api.ValidationService;
+import org.apache.sling.validation.api.Validator;
+import org.apache.sling.validation.api.ValidatorLookupService;
+import org.apache.sling.validation.api.exceptions.SlingValidationException;
+import org.apache.sling.validation.impl.util.JCRBuilder;
+import org.apache.sling.validation.impl.util.Trie;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.query.Query;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component()
+@Service(ValidationService.class)
+public class ValidationServiceImpl implements ValidationService, EventHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ValidationServiceImpl.class);
+
+ static final String MODEL_XPATH_QUERY = "/jcr:root/%s/" + Constants.MODELS_HOME + "*[@sling:resourceType=\"%s\" and @%s=\"%s\"]";
+ static final String[] TOPICS = {SlingConstants.TOPIC_RESOURCE_REMOVED, SlingConstants.TOPIC_RESOURCE_CHANGED,
+ SlingConstants.TOPIC_RESOURCE_ADDED};
+
+ private Map<String, Trie<JCRValidationModel>> validationModelsCache = new ConcurrentHashMap<String, Trie<JCRValidationModel>>();
+ private ThreadPool threadPool;
+ private ServiceRegistration eventHandlerRegistration;
+
+ @Reference
+ private ResourceResolverFactory rrf = null;
+
+ @Reference
+ private ValidatorLookupService validatorLookupService = null;
+
+ @Reference
+ private ThreadPoolManager tpm = null;
+
+ // ValidationService ###################################################################################################################
+ @Override
+ public ValidationModel getValidationModel(String validatedResourceType, String resourcePath) {
+ ValidationModel model = null;
+ Trie<JCRValidationModel> modelsForResourceType = validationModelsCache.get(validatedResourceType);
+ if (modelsForResourceType != null) {
+ model = modelsForResourceType.getElementForLongestMatchingKey(resourcePath).getValue();
+ }
+ if (model == null) {
+ modelsForResourceType = searchAndStoreValidationModel(validatedResourceType);
+ if (modelsForResourceType != null) {
+ model = modelsForResourceType.getElementForLongestMatchingKey(resourcePath).getValue();
+ }
+ }
+ return model;
+ }
+
+ @Override
+ public ValidationModel getValidationModel(Resource resource) {
+ return getValidationModel(resource.getResourceType(), resource.getPath());
+ }
+
+ @Override
+ public ValidationResult validate(Resource resource, ValidationModel model) {
+ if (resource == null || model == null) {
+ throw new IllegalArgumentException("ValidationResult.validate - cannot accept null parameters");
+ }
+ ValidationResultImpl result = new ValidationResultImpl();
+
+ // validate direct properties of the resource
+ validateResourceProperties(resource, resource, model.getResourceProperties(), result);
+
+ // validate children resources, if any
+ for (ChildResource childResource : model.getChildren()) {
+ Resource expectedResource = resource.getChild(childResource.getName());
+ if (expectedResource != null) {
+ validateResourceProperties(resource, expectedResource, childResource.getProperties(), result);
+ } else {
+ result.addFailureMessage(childResource.getName(), "Missing required child resource.");
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public ValidationResult validate(ValueMap valueMap, ValidationModel model) {
+ if (valueMap == null || model == null) {
+ throw new IllegalArgumentException("ValidationResult.validate - cannot accept null parameters");
+ }
+ ValidationResultImpl result = new ValidationResultImpl();
+ for (ResourceProperty resourceProperty : model.getResourceProperties()) {
+ String property = resourceProperty.getName();
+ Object valuesObject = valueMap.get(property);
+ if (valuesObject == null) {
+ result.addFailureMessage(property, "Missing required property.");
+ }
+ Type propertyType = resourceProperty.getType();
+ Map<Validator, Map<String, String>> validators = resourceProperty.getValidators();
+ if (resourceProperty.isMultiple()) {
+ if (valuesObject instanceof String[]) {
+ for (String fieldValue : (String[]) valuesObject) {
+ validatePropertyValue(result, property, fieldValue, propertyType, validators);
+ }
+ } else {
+ result.addFailureMessage(property, "Expected multiple-valued property.");
+ }
+ } else {
+ if (valuesObject instanceof String[]) {
+ // treat request attributes which are arrays
+ String[] fieldValues = (String[]) valuesObject;
+ if (fieldValues.length == 1) {
+ validatePropertyValue(result, property, fieldValues[0], propertyType, validators);
+ } else {
+ result.addFailureMessage(property, "Expected single-valued property.");
+ }
+ } else if (valuesObject instanceof String) {
+ validatePropertyValue(result, property, (String) valuesObject, propertyType, validators);
+ }
+ }
+ }
+ return result;
+ }
+
+ // EventHandler ########################################################################################################################
+ @Override
+ public void handleEvent(Event event) {
+ Runnable task = new Runnable() {
+ @Override
+ public void run() {
+ validationModelsCache.clear();
+ }
+ };
+ threadPool.execute(task);
+ }
+
+ // OSGi ################################################################################################################################
+ @SuppressWarnings("unused")
+ protected void activate(ComponentContext componentContext) {
+ threadPool = tpm.get("Validation Service Thread Pool");
+ ResourceResolver rr = null;
+ try {
+ rr = rrf.getAdministrativeResourceResolver(null);
+ } catch (LoginException e) {
+ LOG.error("Cannot obtain a resource resolver.");
+ }
+ if (rr != null) {
+ StringBuilder sb = new StringBuilder("(");
+ String[] searchPaths = rr.getSearchPath();
+ if (searchPaths.length > 1) {
+ sb.append("|");
+ }
+ for (String searchPath : searchPaths) {
+ if (searchPath.endsWith("/")) {
+ searchPath = searchPath.substring(0, searchPath.length() - 1);
+ }
+ String path = searchPath + "/" + Constants.MODELS_HOME;
+ sb.append("(path=").append(path).append("*)");
+ }
+ sb.append(")");
+ Dictionary<String, Object> eventHandlerProperties = new Hashtable<String, Object>();
+ eventHandlerProperties.put(EventConstants.EVENT_TOPIC, TOPICS);
+ eventHandlerProperties.put(EventConstants.EVENT_FILTER, sb.toString());
+ eventHandlerRegistration = componentContext.getBundleContext().registerService(EventHandler.class.getName(), this,
+ eventHandlerProperties);
+ rr.close();
+ } else {
+ LOG.warn("Null resource resolver. Cannot apply path filtering for event processing. Skipping registering this service as an " +
+ "EventHandler");
+ }
+ }
+
+ @SuppressWarnings("unused")
+ protected void deactivate(ComponentContext componentContext) {
+ if (threadPool != null) {
+ tpm.release(threadPool);
+ }
+ if (eventHandlerRegistration != null) {
+ eventHandlerRegistration.unregister();
+ eventHandlerRegistration = null;
+ }
+ }
+
+ private void validateResourceProperties(Resource rootResource, Resource resource, Set<ResourceProperty> resourceProperties,
+ ValidationResultImpl result) {
+ for (ResourceProperty resourceProperty : resourceProperties) {
+ String property = resourceProperty.getName();
+ ValueMap valueMap = resource.adaptTo(ValueMap.class);
+ Object fieldValues = valueMap.get(property);
+ String relativePath = resource.getPath().replace(rootResource.getPath(), "");
+ if (relativePath.length() > 0) {
+ if (relativePath.startsWith("/")) {
+ relativePath = relativePath.substring(1);
+ }
+ property = relativePath + "/" + property;
+ }
+ if (fieldValues == null) {
+ result.addFailureMessage(property, "Missing required property.");
+ }
+ Type propertyType = resourceProperty.getType();
+ Map<Validator, Map<String, String>> validators = resourceProperty.getValidators();
+ if (fieldValues instanceof String[]) {
+ for (String fieldValue : (String[]) fieldValues) {
+ validatePropertyValue(result, property, fieldValue, propertyType, validators);
+ }
+ } else if (fieldValues instanceof String) {
+ validatePropertyValue(result, property, (String) fieldValues, propertyType, validators);
+ }
+ }
+ }
+
+ /**
+ * Searches for valid validation models in the JCR repository for a certain resource type. All validation models will be returned in a
+ * {@link Trie} data structure for easy retrieval of the models using their {@code applicable paths} as trie keys.
+ * <p/>
+ * A valid content-tree {@code ValidationModel} has the following structure:
+ * <pre>
+ * validationModel
+ * @validatedResourceType
+ * @applicablePaths = [path1,path2,...] (optional)
+ * @sling:resourceType = sling/validation/model
+ * fields
+ * field1
+ * @fieldType
+ * validators
+ * validator1
+ * @validatorArguments = [key=value,key=value...] (optional)
+ * validatorN
+ * #064;validatorArguments = [key=value,key=value...] (optional)
+ * fieldN
+ * @fieldType
+ * validators
+ * validator1
+ * @validatorArguments = [key=value,key=value...] (optional)
+ * </pre>
+ *
+ * @param validatedResourceType the type of resource for which to scan the JCR repository for validation models
+ * @return a {@link Trie} with the validation models; an empty trie if no model is found
+ */
+ private Trie<JCRValidationModel> searchAndStoreValidationModel(String validatedResourceType) {
+ Trie<JCRValidationModel> modelsForResourceType = null;
+ ResourceResolver rr = null;
+ JCRValidationModel vm;
+ try {
+ rr = rrf.getAdministrativeResourceResolver(null);
+ String[] searchPaths = rr.getSearchPath();
+ for (String searchPath : searchPaths) {
+ if (searchPath.endsWith("/")) {
+ searchPath = searchPath.substring(0, searchPath.length() - 1);
+ }
+ final String queryString = String.format(MODEL_XPATH_QUERY, searchPath, Constants.VALIDATION_MODEL_RESOURCE_TYPE,
+ Constants.VALIDATED_RESOURCE_TYPE, validatedResourceType);
+ Iterator<Resource> models = rr.findResources(queryString, Query.XPATH);
+ while (models.hasNext()) {
+ Resource model = models.next();
+ LOG.info("Found validation model resource {}.", model.getPath());
+ String jcrPath = model.getPath();
+ ValueMap validationModelProperties = model.adaptTo(ValueMap.class);
+ String[] applicablePaths = PropertiesUtil.toStringArray(validationModelProperties.get(Constants.APPLICABLE_PATHS,
+ String[].class));
+ if (validatedResourceType != null && !"".equals(validatedResourceType)) {
+ Resource r = model.getChild(Constants.PROPERTIES);
+ if (r != null) {
+ Set<ResourceProperty> resourceProperties = JCRBuilder.buildProperties(validatorLookupService, r);
+ if (!resourceProperties.isEmpty()) {
+ List<ChildResource> children = JCRBuilder.buildChildren(model, model, validatorLookupService);
+ vm = new JCRValidationModel(jcrPath, resourceProperties, validatedResourceType, applicablePaths, children);
+ modelsForResourceType = validationModelsCache.get(validatedResourceType);
+ /**
+ * if the modelsForResourceType is null the canAcceptModel will return true: performance optimisation so that
+ * the Trie is created only if the model is accepted
+ */
+
+ if (canAcceptModel(vm, searchPath, searchPaths, modelsForResourceType)) {
+ if (modelsForResourceType == null) {
+ modelsForResourceType = new Trie<JCRValidationModel>();
+ validationModelsCache.put(validatedResourceType, modelsForResourceType);
+ }
+ for (String applicablePath : vm.getApplicablePaths()) {
+ modelsForResourceType.insert(applicablePath, vm);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (LoginException e) {
+ LOG.error("Unable to obtain a resource resolver.", e);
+ }
+ if (rr != null) {
+ rr.close();
+ }
+ return modelsForResourceType;
+ }
+
+ /**
+ * Checks if the {@code validationModel} does not override an existing stored model given the fact that the overlaying is done based on
+ * the order in which the search paths are in the {@code searchPaths} array: the lower the index, the higher the priority.
+ *
+ * @param validationModel the model to be checked
+ * @param currentSearchPath the current search path
+ * @param searchPaths the available search paths
+ * @param validationModels the existing validation models
+ * @return {@code true} if the new model can be stored, {@code false} otherwise
+ */
+ private boolean canAcceptModel(JCRValidationModel validationModel, String currentSearchPath, String[] searchPaths,
+ Trie<JCRValidationModel> validationModels) {
+ // perform null check to optimise performance in callee - no need to previously create the Trie if we're not going to accept the model
+ if (validationModels != null) {
+ String relativeModelPath = validationModel.getJcrPath().replaceFirst(currentSearchPath, "");
+ for (String searchPath : searchPaths) {
+ if (!currentSearchPath.equals(searchPath)) {
+ for (String applicablePath : validationModel.getApplicablePaths()) {
+ JCRValidationModel existingVM = validationModels.getElement(applicablePath).getValue();
+ if (existingVM != null) {
+ String existingModelRelativeModelPath = existingVM.getJcrPath().replaceFirst(searchPath, "");
+ if (existingModelRelativeModelPath.equals(relativeModelPath)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private void validatePropertyValue(ValidationResultImpl result, String property, String value, Type propertyType, Map<Validator,
+ Map<String, String>> validators) {
+ if (!propertyType.isValid(value)) {
+ result.addFailureMessage(property, "Property was expected to be of type " + propertyType.getName());
+ }
+ for (Map.Entry<Validator, Map<String, String>> validatorEntry : validators.entrySet()) {
+ Validator validator = validatorEntry.getKey();
+ Map<String, String> arguments = validatorEntry.getValue();
+ try {
+ if (!validator.validate(value, arguments)) {
+ result.addFailureMessage(property, "Property does not contain a valid value for the " + validator
+ .getClass().getName() + " validator");
+ }
+ } catch (SlingValidationException e) {
+ LOG.error("SlingValidationException for resourceProperty " + property, e);
+ result.addFailureMessage(property, "Validator " + validator.getClass() + "encountered a problem: " + e.getMessage());
+ }
+ }
+ }
+}
\ No newline at end of file
Added: sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/ValidatorLookupServiceImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/ValidatorLookupServiceImpl.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/ValidatorLookupServiceImpl.java (added)
+++ sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/ValidatorLookupServiceImpl.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,56 @@
+/*
+ * 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 org.apache.sling.validation.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.validation.api.Validator;
+import org.apache.sling.validation.api.ValidatorLookupService;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component()
+@Service(ValidatorLookupService.class)
+@Reference(
+ name = "validator",
+ referenceInterface = Validator.class,
+ policy = ReferencePolicy.DYNAMIC,
+ cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE
+)
+public class ValidatorLookupServiceImpl implements ValidatorLookupService {
+
+ Map<String, Validator> validators = new ConcurrentHashMap<String, Validator>();
+
+ public Validator getValidator(String validatorType) {
+ return validators.get(validatorType);
+ }
+
+ // OSGi ################################################################################################################################
+ protected void bindValidator(Validator validator, Map<?, ?> properties) {
+ validators.put(validator.getClass().getName(), validator);
+ }
+
+ protected void unbindValidator(Validator validator, Map<?, ?> properties) {
+ validators.remove(validator.getClass().getName());
+ }
+}
Added: sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/JCRBuilder.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/JCRBuilder.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/JCRBuilder.java (added)
+++ sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/JCRBuilder.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,115 @@
+/*
+ * 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 org.apache.sling.validation.impl.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.validation.api.ChildResource;
+import org.apache.sling.validation.api.ResourceProperty;
+import org.apache.sling.validation.api.Type;
+import org.apache.sling.validation.api.Validator;
+import org.apache.sling.validation.api.ValidatorLookupService;
+import org.apache.sling.validation.impl.ChildResourceImpl;
+import org.apache.sling.validation.impl.Constants;
+import org.apache.sling.validation.impl.ResourcePropertyImpl;
+
+/**
+ * Helps building validation related objects from JCR content trees.
+ */
+public class JCRBuilder {
+
+ /**
+ * Creates a set of the properties that a resource is expected to have, together with the associated validators.
+ *
+ * @param vls the {@link ValidatorLookupService}
+ * @param propertiesResource the resource identifying the properties node from a validation model's structure
+ * @return a set of properties or an empty set if no properties are defined
+ * @see ResourceProperty
+ */
+ public static Set<ResourceProperty> buildProperties(ValidatorLookupService vls, Resource propertiesResource) {
+ Set<ResourceProperty> properties = new HashSet<ResourceProperty>();
+ if (propertiesResource != null) {
+ for (Resource property : propertiesResource.getChildren()) {
+ String fieldName = property.getName();
+ ValueMap propertyValueMap = property.adaptTo(ValueMap.class);
+ Type type = Type.getType(propertyValueMap.get(Constants.PROPERTY_TYPE, String.class));
+ Boolean propertyMultiple = PropertiesUtil.toBoolean(propertyValueMap.get(Constants.PROPERTY_MULTIPLE), false);
+ Resource validators = property.getChild(Constants.VALIDATORS);
+ Map<Validator, Map<String, String>> validatorsMap = new HashMap<Validator, Map<String, String>>();
+ if (validators != null) {
+ Iterator<Resource> validatorsIterator = validators.listChildren();
+ while (validatorsIterator.hasNext()) {
+ Resource validator = validatorsIterator.next();
+ ValueMap validatorProperties = validator.adaptTo(ValueMap.class);
+ String validatorName = validator.getName();
+ Validator v = vls.getValidator(validatorName);
+ String[] validatorArguments = validatorProperties.get(Constants.VALIDATOR_ARGUMENTS, String[].class);
+ Map<String, String> validatorArgumentsMap = new HashMap<String, String>();
+ if (validatorArguments != null) {
+ for (String arg : validatorArguments) {
+ String[] keyValuePair = arg.split("=");
+ if (keyValuePair.length != 2) {
+ continue;
+ }
+ validatorArgumentsMap.put(keyValuePair[0], keyValuePair[1]);
+ }
+ }
+ validatorsMap.put(v, validatorArgumentsMap);
+ }
+ }
+ ResourceProperty f = new ResourcePropertyImpl(fieldName, type, propertyMultiple, validatorsMap);
+ properties.add(f);
+ }
+ }
+ return properties;
+ }
+
+ /**
+ * Searches children resources from a {@code modelResource}, starting from the {@code rootResource}. If one needs all the children
+ * resources of a model, then the {@code modelResource} and the {@code rootResource} should be identical.
+ *
+ * @param modelResource the resource describing a {@link org.apache.sling.validation.api.ValidationModel}
+ * @param rootResource the model's resource from which to search for children (this resource has to have a {@link
+ * Constants#CHILDREN} node directly underneath it)
+ * @param validatorLookupService the {@link ValidatorLookupService}
+ * @return a list of all the children resources; the list will be empty if there are no children resources
+ */
+ public static List<ChildResource> buildChildren(Resource modelResource, Resource rootResource,
+ ValidatorLookupService validatorLookupService) {
+ List<ChildResource> children = new ArrayList<ChildResource>();
+ Resource childrenResource = rootResource.getChild(Constants.CHILDREN);
+ if (childrenResource != null) {
+ for (Resource child : childrenResource.getChildren()) {
+ ChildResource childResource = new ChildResourceImpl(modelResource, child, validatorLookupService);
+ children.add(childResource);
+ children.addAll(buildChildren(modelResource, child, validatorLookupService));
+ }
+ }
+ return children;
+ }
+}
Added: sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/Trie.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/Trie.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/Trie.java (added)
+++ sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/Trie.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,115 @@
+/*
+ * 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 org.apache.sling.validation.impl.util;
+
+import java.util.Map;
+
+/**
+ * Trie data structure used for storing objects using {@link String} keys that allows object retrieval using a longest matching key
+ * mechanism.
+ */
+public class Trie<T> {
+
+ /**
+ * The {@code ROOT} node of the Trie, initialised with the "null" character.
+ */
+ public final TrieNode<T> ROOT = new TrieNode<T>('\0');
+
+ /**
+ * Inserts an object {@link T} under the specified {@code key}.
+ *
+ * @param key the key under which the object will be stored
+ * @param value the object to be stored
+ */
+ public void insert(String key, T value) {
+ if (key != null && !"".equals(key)) {
+ int length = key.length();
+ TrieNode<T> node = ROOT;
+ for (int index = 0; index < length; index++) {
+ Map<Character, TrieNode<T>> children = node.getChildren();
+ char character = key.charAt(index);
+ node = children.get(character);
+ if (node == null) {
+ node = new TrieNode<T>(character);
+ children.put(character, node);
+ }
+ }
+ node.setLeaf(true);
+ node.setValue(value);
+ }
+ }
+
+ /**
+ * Retrieves the {@link TrieNode} stored under the best matching key.
+ *
+ * @param key the key; if the key doesn't match with an existing key, the best matching key will be used for retrieval; if no match is
+ * found the {@link Trie#ROOT} node will be returned.
+ * @return the {@link TrieNode} stored under the best matching key or the {@link Trie#ROOT} node if no match was found
+ */
+ public TrieNode<T> getElementForLongestMatchingKey(String key) {
+ TrieNode<T> result = ROOT;
+ if (key != null && !"".equals(key)) {
+ int length = key.length();
+ TrieNode<T> node = ROOT;
+ for (int index = 0; index < length; index++) {
+ char character = key.charAt(index);
+ Map<Character, TrieNode<T>> children = node.getChildren();
+ node = children.get(character);
+ if (node != null) {
+ if (node.isLeaf()) {
+ result = node;
+ }
+ } else {
+ break;
+ }
+ }
+
+ }
+ return result;
+ }
+
+ /**
+ * Returns the {@link TrieNode} stored under the given {@code key}. If no element is stored under that key, the {@link Trie#ROOT} node
+ * will be returned.
+ * @param key the key
+ * @return the {@link TrieNode} stored under the given key or the {@link Trie#ROOT} if no node is found under that {@code key}
+ */
+ public TrieNode<T> getElement(String key) {
+ TrieNode<T> result = ROOT;
+ TrieNode<T> node = null;
+ boolean nodeExists = true;
+ if (key != null && !"".equals(key)) {
+ int length = key.length();
+ node = ROOT;
+ for (int index = 0; index < length; index++) {
+ char character = key.charAt(index);
+ Map<Character, TrieNode<T>> children = node.getChildren();
+ node = children.get(character);
+ if (node == null) {
+ nodeExists = false;
+ break;
+ }
+ }
+ }
+ if (nodeExists) {
+ result = node;
+ }
+ return result;
+ }
+}
Added: sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/TrieNode.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/TrieNode.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/TrieNode.java (added)
+++ sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/util/TrieNode.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,65 @@
+/*
+ * 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 org.apache.sling.validation.impl.util;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implements a Trie node.
+ */
+public class TrieNode<T> {
+
+ private char character;
+ private T value;
+ private Map<Character, TrieNode<T>> children;
+ private boolean isLeaf;
+
+ TrieNode(char ch) {
+ character = ch;
+ children = new ConcurrentHashMap<Character, TrieNode<T>>();
+ isLeaf = false;
+ }
+
+ public char getCharacter() {
+ return character;
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ public void setValue(T value) {
+ this.value = value;
+ }
+
+ public Map<Character, TrieNode<T>> getChildren() {
+ return children;
+ }
+
+ public boolean isLeaf() {
+ return isLeaf;
+ }
+
+ public void setLeaf(boolean isLeaf) {
+ this.isLeaf = isLeaf;
+ }
+
+
+}
Added: sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/validators/RegexValidator.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/validators/RegexValidator.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/validators/RegexValidator.java (added)
+++ sling/trunk/contrib/validation/core/src/main/java/org/apache/sling/validation/impl/validators/RegexValidator.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,51 @@
+/*
+ * 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 org.apache.sling.validation.impl.validators;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.validation.api.Validator;
+import org.apache.sling.validation.api.exceptions.SlingValidationException;
+
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Performs regular expressions validation on the supplied data with the help of the {@link Pattern} class. This {@code Validator} expects a
+ * mandatory parameter in the arguments map: {@link RegexValidator#REGEX_PARAM}.
+ */
+@Component()
+@Service(Validator.class)
+public class RegexValidator implements Validator {
+
+ public static final String REGEX_PARAM = "regex";
+
+ @Override
+ public boolean validate(String data, Map<String, String> arguments) {
+ if (data == null || arguments == null) {
+ throw new SlingValidationException("Cannot perform data validation with null parameters");
+ }
+ String regex = arguments.get(REGEX_PARAM);
+ if (regex == null) {
+ throw new SlingValidationException("Mandatory " + REGEX_PARAM + " is missing from the arguments map.");
+ }
+ Pattern pattern = Pattern.compile(regex);
+ return pattern.matcher(data).matches();
+ }
+}
Added: sling/trunk/contrib/validation/core/src/main/resources/OSGI-INF/metatype/metatype.properties
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/main/resources/OSGI-INF/metatype/metatype.properties?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/main/resources/OSGI-INF/metatype/metatype.properties (added)
+++ sling/trunk/contrib/validation/core/src/main/resources/OSGI-INF/metatype/metatype.properties Tue Oct 7 15:10:37 2014
@@ -0,0 +1,5 @@
+validationservice.label = Sling Validation Service
+validationservice.description = The Sling Validation Service is responsible for locating Sling Validators
+
+alphacharactersvalidator.label = Alpha Characters Validator
+alphacharactersvalidator.description = The Alpha Characters Validator checks that submitted data contains only Unicode letters
\ No newline at end of file
Added: sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/ValidationServiceImplTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/ValidationServiceImplTest.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/ValidationServiceImplTest.java (added)
+++ sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/ValidationServiceImplTest.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,423 @@
+/*
+ * 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 org.apache.sling.validation.impl;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.jcr.resource.JcrResourceConstants;
+import org.apache.sling.validation.api.Type;
+import org.apache.sling.validation.api.ValidationModel;
+import org.apache.sling.validation.api.ValidationResult;
+import org.apache.sling.validation.api.ValidationService;
+import org.apache.sling.validation.api.ValidatorLookupService;
+import org.apache.sling.validation.impl.setup.MockedResourceResolver;
+import org.apache.sling.validation.impl.validators.RegexValidator;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.powermock.reflect.Whitebox;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ValidationServiceImplTest {
+
+ /**
+ * Assume the validation models are stored under (/libs|/apps) + / + VALIDATION_MODELS_RELATIVE_PATH.
+ */
+ private static final String VALIDATION_MODELS_RELATIVE_PATH = "sling/validation/models";
+ private static final String APPS = "/apps";
+ private static final String LIBS = "/libs";
+ private static ResourceResolverFactory rrf;
+ private static Resource appsValidatorsRoot;
+ private static Resource libsValidatorsRoot;
+ private ValidationService validationService;
+ private ValidatorLookupService validatorLookupService;
+
+ @BeforeClass
+ public static void init() throws Exception {
+ rrf = mock(ResourceResolverFactory.class);
+ when(rrf.getAdministrativeResourceResolver(null)).thenAnswer(new Answer<ResourceResolver>() {
+ public ResourceResolver answer(InvocationOnMock invocation) throws Throwable {
+ return new MockedResourceResolver();
+ }
+ });
+ ResourceResolver rr = rrf.getAdministrativeResourceResolver(null);
+ if (rr != null) {
+ appsValidatorsRoot = ResourceUtil.getOrCreateResource(rr, APPS + "/" + VALIDATION_MODELS_RELATIVE_PATH, (Map) null,
+ "sling:Folder", true);
+ libsValidatorsRoot = ResourceUtil.getOrCreateResource(rr, LIBS + "/" + VALIDATION_MODELS_RELATIVE_PATH, (Map) null,
+ "sling:Folder", true);
+ rr.close();
+ }
+ }
+
+ @AfterClass
+ public static void beNiceAndClean() throws Exception {
+ ResourceResolver rr = rrf.getAdministrativeResourceResolver(null);
+ if (rr != null) {
+ if (appsValidatorsRoot != null) {
+ rr.delete(appsValidatorsRoot);
+ }
+ if (libsValidatorsRoot != null) {
+ rr.delete(libsValidatorsRoot);
+ }
+ rr.commit();
+ rr.close();
+ }
+ }
+
+ @Before
+ public void setUp() {
+ validationService = new ValidationServiceImpl();
+ Whitebox.setInternalState(validationService, "rrf", rrf);
+ validatorLookupService = mock(ValidatorLookupService.class);
+ }
+
+ @Test
+ public void testGetValidationModel() throws Exception {
+ when(validatorLookupService.getValidator("org.apache.sling.validation.impl.validators.RegexValidator")).thenReturn(new
+ RegexValidator());
+ Whitebox.setInternalState(validationService, "validatorLookupService", validatorLookupService);
+
+ List<TestProperty> properties = new ArrayList<TestProperty>();
+ TestProperty property = new TestProperty();
+ property.name = "field1";
+ property.type = Type.DATE;
+ property.validators.put("org.apache.sling.validation.impl.validators.RegexValidator", null);
+ properties.add(property);
+ ResourceResolver rr = rrf.getAdministrativeResourceResolver(null);
+ Resource model1 = null, model2 = null;
+ try {
+ if (rr != null) {
+ model1 = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", "sling/validation/test",
+ new String[]{"/apps/validation"}, properties);
+ model2 = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel2", "sling/validation/test",
+ new String[]{"/apps/validation/1",
+ "/apps/validation/2"}, properties);
+ }
+
+ // BEST MATCHING PATH = /apps/validation/1; assume the applicable paths contain /apps/validation/2
+ ValidationModel vm = validationService.getValidationModel("sling/validation/test", "/apps/validation/1/resource");
+ assertTrue(arrayContainsString(vm.getApplicablePaths(), "/apps/validation/2"));
+
+ // BEST MATCHING PATH = /apps/validation; assume the applicable paths contain /apps/validation but not /apps/validation/1
+ vm = validationService.getValidationModel("sling/validation/test", "/apps/validation/resource");
+ assertTrue(arrayContainsString(vm.getApplicablePaths(), "/apps/validation"));
+ assertTrue(!arrayContainsString(vm.getApplicablePaths(), "/apps/validation/1"));
+ if (model1 != null) {
+ rr.delete(model1);
+ }
+ if (model2 != null) {
+ rr.delete(model2);
+ }
+ } finally {
+ if (rr != null) {
+ rr.commit();
+ rr.close();
+ }
+ }
+ }
+
+ @Test
+ public void testGetValidationModelWithOverlay() throws Exception {
+ when(validatorLookupService.getValidator("org.apache.sling.validation.impl.validators.RegexValidator")).thenReturn(new
+ RegexValidator());
+ Whitebox.setInternalState(validationService, "validatorLookupService", validatorLookupService);
+
+ List<TestProperty> fields = new ArrayList<TestProperty>();
+ TestProperty field = new TestProperty();
+ field.name = "field1";
+ field.type = Type.DATE;
+ field.validators.put("org.apache.sling.validation.impl.validators.RegexValidator", null);
+ fields.add(field);
+ ResourceResolver rr = rrf.getAdministrativeResourceResolver(null);
+ Resource model1 = null, model2 = null, model3 = null;
+ try {
+ if (rr != null) {
+ model1 = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", "sling/validation/test",
+ new String[]{"/apps/validation/1"}, fields);
+ model2 = createValidationModelResource(rr, appsValidatorsRoot.getPath(), "testValidationModel1", "sling/validation/test",
+ new String[]{"/apps/validation/1",
+ "/apps/validation/2"}, fields);
+ model3 = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel2", "sling/validation/test",
+ new String[]{"/apps/validation/3"}, fields);
+ }
+
+ // BEST MATCHING PATH = /apps/validation/1; assume the applicable paths contain /apps/validation/2
+ ValidationModel vm = validationService.getValidationModel("sling/validation/test", "/apps/validation/1/resource");
+ assertTrue(arrayContainsString(vm.getApplicablePaths(), "/apps/validation/2"));
+
+ vm = validationService.getValidationModel("sling/validation/test", "/apps/validation/3/resource");
+ assertTrue(arrayContainsString(vm.getApplicablePaths(), "/apps/validation/3"));
+
+ if (model1 != null) {
+ rr.delete(model1);
+ }
+ if (model2 != null) {
+ rr.delete(model2);
+ }
+ if (model3 != null) {
+ rr.delete(model3);
+ }
+ } finally {
+ if (rr != null) {
+ rr.commit();
+ rr.close();
+ }
+ }
+ }
+
+ @Test
+ public void testValueMapWithWrongDataType() throws Exception {
+ when(validatorLookupService.getValidator("org.apache.sling.validation.impl.validators.RegexValidator")).thenReturn(new
+ RegexValidator());
+ Whitebox.setInternalState(validationService, "validatorLookupService", validatorLookupService);
+
+ List<TestProperty> properties = new ArrayList<TestProperty>();
+ TestProperty property = new TestProperty();
+ property.name = "field1";
+ property.type = Type.DATE;
+ property.validators.put("org.apache.sling.validation.impl.validators.RegexValidator", null);
+ properties.add(property);
+ ResourceResolver rr = rrf.getAdministrativeResourceResolver(null);
+ Resource model1 = null;
+ try {
+ if (rr != null) {
+ model1 = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", "sling/validation/test",
+ new String[]{"/apps/validation"}, properties);
+ }
+ ValidationModel vm = validationService.getValidationModel("sling/validation/test", "/apps/validation/1/resource");
+ HashMap<String, Object> hashMap = new HashMap<String, Object>() {{
+ put("field1", "1");
+ }};
+ ValueMap map = new ValueMapDecorator(hashMap);
+ ValidationResult vr = validationService.validate(map, vm);
+ assertFalse(vr.isValid());
+ if (model1 != null) {
+ rr.delete(model1);
+ }
+ } finally {
+ if (rr != null) {
+ rr.commit();
+ rr.close();
+ }
+ }
+ }
+
+ @Test
+ public void testValueMapWithCorrectDataType() throws Exception {
+ when(validatorLookupService.getValidator("org.apache.sling.validation.impl.validators.RegexValidator")).thenReturn(new
+ RegexValidator());
+ Whitebox.setInternalState(validationService, "validatorLookupService", validatorLookupService);
+
+ List<TestProperty> fields = new ArrayList<TestProperty>();
+ TestProperty field = new TestProperty();
+ field.name = "field1";
+ field.type = Type.STRING;
+ field.validators.put("org.apache.sling.validation.impl.validators.RegexValidator", new String[] {"regex=^\\p{L}+$"});
+ fields.add(field);
+ ResourceResolver rr = rrf.getAdministrativeResourceResolver(null);
+ Resource model1 = null;
+ try {
+ if (rr != null) {
+ model1 = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", "sling/validation/test",
+ new String[]{"/apps/validation"}, fields);
+ }
+ ValidationModel vm = validationService.getValidationModel("sling/validation/test", "/apps/validation/1/resource");
+ HashMap<String, Object> hashMap = new HashMap<String, Object>() {{
+ put("field1", "HelloWorld");
+ }};
+ ValueMap map = new ValueMapDecorator(hashMap);
+ ValidationResult vr = validationService.validate(map, vm);
+ assertTrue(vr.isValid());
+ if (model1 != null) {
+ rr.delete(model1);
+ }
+ } finally {
+ if (rr != null) {
+ rr.commit();
+ rr.close();
+ }
+ }
+ }
+
+ @Test
+ public void testResourceWithMissingChildProperty() throws Exception {
+ when(validatorLookupService.getValidator("org.apache.sling.validation.impl.validators.RegexValidator")).thenReturn(new
+ RegexValidator());
+ Whitebox.setInternalState(validationService, "validatorLookupService", validatorLookupService);
+
+ List<TestProperty> fields = new ArrayList<TestProperty>();
+ TestProperty property = new TestProperty();
+ property.name = "field1";
+ property.type = Type.INT;
+ property.validators.put("org.apache.sling.validation.impl.validators.RegexValidator", new String[] {RegexValidator.REGEX_PARAM + "=" + "\\d"});
+ fields.add(property);
+ ResourceResolver rr = rrf.getAdministrativeResourceResolver(null);
+ Resource model1 = null;
+ Resource testResource = null;
+ try {
+ if (rr != null) {
+ model1 = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", "sling/validation/test",
+ new String[]{"/apps/validation"}, fields);
+ Resource modelChildren = rr.create(model1, "children", new HashMap<String, Object>(){{
+ put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ }});
+ Resource child = rr.create(modelChildren, "child1", new HashMap<String, Object>(){{
+ put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ }});
+ Resource childProperties = rr.create(child, "properties", new HashMap<String, Object>(){{
+ put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ }});
+ Resource childProperty = rr.create(childProperties, "hello", new HashMap<String, Object>(){{
+ put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ put(Constants.PROPERTY_TYPE, "string");
+ }});
+ Resource grandChildren = rr.create(child, "children", new HashMap<String, Object>(){{
+ put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ }});
+ Resource grandChild = rr.create(grandChildren, "grandChild1", new HashMap<String, Object>(){{
+ put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ }});
+
+ testResource = ResourceUtil.getOrCreateResource(rr, "/apps/validation/1/resource", JcrConstants.NT_UNSTRUCTURED,
+ JcrConstants.NT_UNSTRUCTURED, true);
+ Resource childResource = rr.create(testResource, "child1", new HashMap<String, Object>(){{
+ put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ }});
+ rr.commit();
+
+ ModifiableValueMap mvm = testResource.adaptTo(ModifiableValueMap.class);
+ mvm.put("field1", "1");
+ rr.commit();
+
+ // /apps/validation/1/resource/child1 will miss its mandatory "hello" property
+ Resource resourceChild = rr.create(testResource, "child1", new HashMap<String, Object>(){{
+ put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ }});
+
+ Resource resourceGrandChild = rr.create(resourceChild, "grandChild1", new HashMap<String, Object>(){{
+ put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ }});
+ rr.commit();
+ }
+ ValidationModel vm = validationService.getValidationModel("sling/validation/test", "/apps/validation/1/resource");
+ ValidationResult vr = validationService.validate(testResource, vm);
+ assertFalse(vr.isValid());
+ assertTrue(vr.getFailureMessages().containsKey("child1/hello"));
+ } finally {
+ if (rr != null) {
+ if (model1 != null) {
+ rr.delete(model1);
+ }
+ if (testResource != null) {
+ rr.delete(testResource);
+ }
+ rr.commit();
+ rr.close();
+ }
+ }
+ }
+
+ private Resource createValidationModelResource(ResourceResolver rr, String root, String name, String validatedResourceType,
+ String[] applicableResourcePaths, List<TestProperty> properties) throws Exception {
+ Map<String, Object> modelProperties = new HashMap<String, Object>();
+ modelProperties.put(Constants.VALIDATED_RESOURCE_TYPE, validatedResourceType);
+ modelProperties.put(Constants.APPLICABLE_PATHS, applicableResourcePaths);
+ modelProperties.put(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, Constants.VALIDATION_MODEL_RESOURCE_TYPE);
+ modelProperties.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ Resource model = ResourceUtil.getOrCreateResource(rr, root + "/" + name, modelProperties, JcrResourceConstants.NT_SLING_FOLDER, true);
+ if (model != null) {
+ Resource propertiesResource = ResourceUtil.getOrCreateResource(rr, model.getPath() + "/" + Constants
+ .PROPERTIES, JcrConstants.NT_UNSTRUCTURED, null, true);
+ if (propertiesResource != null) {
+ for (TestProperty property : properties) {
+ Map<String, Object> modelPropertyJCRProperties = new HashMap<String, Object>();
+ modelPropertyJCRProperties.put(Constants.PROPERTY_TYPE, property.type.getName());
+ modelPropertyJCRProperties.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ Resource propertyResource = ResourceUtil.getOrCreateResource(rr, propertiesResource.getPath() + "/" + property.name,
+ modelPropertyJCRProperties, null, true);
+ if (propertyResource != null) {
+ Resource validators = ResourceUtil.getOrCreateResource(rr,
+ propertyResource.getPath() + "/" + Constants.VALIDATORS,
+ JcrConstants.NT_UNSTRUCTURED, null, true);
+ if (validators != null) {
+ for (Map.Entry<String, String[]> v : property.validators.entrySet()) {
+ Map<String, Object> validatorProperties = new HashMap<String, Object>();
+ validatorProperties.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
+ if (v.getValue() != null) {
+ validatorProperties.put(Constants.VALIDATOR_ARGUMENTS, v.getValue());
+ }
+ ResourceUtil.getOrCreateResource(rr, validators.getPath() + "/" + v.getKey(), validatorProperties, null,
+ true);
+ }
+ }
+ }
+ }
+ }
+ }
+ return model;
+ }
+
+ private boolean arrayContainsString(String[] array, String string) {
+ boolean result = false;
+ if (array != null && string != null) {
+ for (String s : array) {
+ if (string.equals(s)) {
+ result = true;
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ private class TestProperty {
+ String name;
+ Type type;
+ Map<String, String[]> validators;
+
+ TestProperty() {
+ validators = new HashMap<String, String[]>();
+ }
+ }
+
+ private class TestChild {
+
+ }
+
+}
Added: sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/setup/MockedResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/setup/MockedResource.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/setup/MockedResource.java (added)
+++ sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/setup/MockedResource.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,261 @@
+/*
+ * 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 org.apache.sling.validation.impl.setup;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.jcr.resource.JcrResourceConstants;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class MockedResource extends SyntheticResource {
+
+ private final MockedResourceResolver mockedResourceResolver;
+ private Session session;
+
+ public MockedResource(MockedResourceResolver resourceResolver, Node node) throws RepositoryException {
+ super(resourceResolver, node.getPath(), node.getProperty("./sling:resourceType").getString() != null ? node.getProperty("./" +
+ JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY).getString() : node.getProperty(JcrConstants.JCR_PRIMARYTYPE).getString
+ ());
+ mockedResourceResolver = resourceResolver;
+
+ }
+
+ public MockedResource(MockedResourceResolver resourceResolver, String path,
+ String resourceType) {
+ super(resourceResolver, path, resourceType);
+ mockedResourceResolver = resourceResolver;
+ resourceResolver.register(this);
+ }
+
+ private Session getSession() {
+ synchronized (this) {
+ if (session == null) {
+ try {
+ session = mockedResourceResolver.createSession();
+ } catch (RepositoryException e) {
+ throw new RuntimeException("RepositoryException: " + e, e);
+ }
+ }
+ return session;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ close();
+ super.finalize();
+ }
+
+ public void close() {
+ synchronized (this) {
+ if (session != null) {
+ if (session.isLive()) {
+ session.logout();
+ }
+ session = null;
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ if (type.equals(Node.class)) {
+ try {
+ return (AdapterType) getSession().getNode(getPath());
+ } catch (Exception e) {
+ throw new RuntimeException("Exception occurred: " + e, e);
+ }
+ } else if (type.equals(ValueMap.class)) {
+ try {
+ Session session = getSession();
+ Node node = session.getNode(getPath());
+ HashMap<String, Object> map = new HashMap<String, Object>();
+
+ PropertyIterator properties = node.getProperties();
+ while (properties.hasNext()) {
+ Property p = properties.nextProperty();
+ List valuesList;
+ if (p.isMultiple()) {
+ switch (p.getType()) {
+ case PropertyType.STRING:
+ valuesList = new ArrayList<String>();
+ for (Value v : p.getValues()) {
+ valuesList.add(v.getString());
+ }
+ map.put(p.getName(), valuesList.toArray());
+ break;
+ case PropertyType.NAME:
+ valuesList = new ArrayList<String>();
+ for (Value v : p.getValues()) {
+ valuesList.add(v.getString());
+ }
+ map.put(p.getName(), valuesList.toArray());
+ break;
+ }
+ } else if (p.getType() == PropertyType.BOOLEAN) {
+ map.put(p.getName(), p.getBoolean());
+ } else if (p.getType() == PropertyType.STRING) {
+ map.put(p.getName(), p.getString());
+ } else if (p.getType() == PropertyType.DATE) {
+ map.put(p.getName(), p.getDate().getTime());
+ } else if (p.getType() == PropertyType.NAME) {
+ map.put(p.getName(), p.getName());
+ } else {
+ throw new RuntimeException(
+ "Unsupported property type: " + p.getType());
+ }
+ }
+ ValueMap valueMap = new ValueMapDecorator(map);
+ return (AdapterType) valueMap;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ } else if (type.equals(ModifiableValueMap.class)) {
+ return (AdapterType) new ModifiableValueMap() {
+
+ public Collection<Object> values() {
+ throw new UnsupportedOperationException();
+ }
+
+ public int size() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object remove(Object arg0) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void putAll(Map<? extends String, ? extends Object> arg0) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object put(String arg0, Object arg1) {
+ Session session = getSession();
+ try {
+ final Node node = session.getNode(getPath());
+ Object result = null;
+ if (node.hasProperty(arg0)) {
+ final Property previous = node.getProperty(arg0);
+ if (previous == null) {
+ // null
+ } else if (previous.getType() == PropertyType.STRING) {
+ result = previous.getString();
+ } else if (previous.getType() == PropertyType.DATE) {
+ result = previous.getDate();
+ } else if (previous.getType() == PropertyType.BOOLEAN) {
+ result = previous.getBoolean();
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ if (arg1 instanceof String) {
+ node.setProperty(arg0, (String) arg1);
+ } else if (arg1 instanceof Calendar) {
+ node.setProperty(arg0, (Calendar) arg1);
+ } else if (arg1 instanceof Boolean) {
+ node.setProperty(arg0, (Boolean) arg1);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ return result;
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Set<String> keySet() {
+ Session session = getSession();
+ try {
+ final Node node = session.getNode(getPath());
+ final PropertyIterator pi = node.getProperties();
+ final Set<String> result = new HashSet<String>();
+ while (pi.hasNext()) {
+ final Property p = pi.nextProperty();
+ result.add(p.getName());
+ }
+ return result;
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public boolean isEmpty() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object get(Object arg0) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Set<Entry<String, Object>> entrySet() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean containsValue(Object arg0) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean containsKey(Object arg0) {
+ Session session = getSession();
+ try {
+ final Node node = session.getNode(getPath());
+ return node.hasProperty(String.valueOf(arg0));
+ } catch (RepositoryException re) {
+ throw new RuntimeException(re);
+ }
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ public <T> T get(String name, T defaultValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ public <T> T get(String name, Class<T> type) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ } else {
+ return super.adaptTo(type);
+ }
+ }
+
+}
Added: sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/setup/MockedResourceResolver.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/setup/MockedResourceResolver.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/setup/MockedResourceResolver.java (added)
+++ sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/setup/MockedResourceResolver.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,309 @@
+/*
+ * 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 org.apache.sling.validation.impl.setup;
+
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.commons.testing.jcr.RepositoryProvider;
+import org.apache.sling.commons.testing.jcr.RepositoryUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class MockedResourceResolver implements ResourceResolver {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MockedResourceResolver.class);
+
+ private static final String[] SEARCH_PATHS = new String[] {"/apps", "/libs"};
+
+ public final RepositoryProvider repoProvider;
+ private List<MockedResource> resources = new LinkedList<MockedResource>();
+
+ private Session session;
+
+ public MockedResourceResolver() throws Exception {
+ this.repoProvider = RepositoryProvider.instance();
+ createSession();
+ RepositoryUtil.registerSlingNodeTypes(session);
+ }
+
+ public Session createSession() throws RepositoryException {
+ synchronized (this) {
+ if (session != null) {
+ return session;
+ }
+ session = repoProvider.getRepository().loginAdministrative(null);
+ return session;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ if (type.equals(Session.class)) {
+ try {
+ return (AdapterType) createSession();
+ } catch (RepositoryException e) {
+ throw new RuntimeException("RepositoryException: " + e, e);
+ }
+ } else if (type.equals(Repository.class)) {
+ try {
+ return (AdapterType) repoProvider.getRepository();
+ } catch (RepositoryException e) {
+ throw new RuntimeException("RepositoryException: " + e, e);
+ }
+ }
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public Resource resolve(HttpServletRequest request, String absPath) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public Resource resolve(String absPath) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Deprecated
+ public Resource resolve(HttpServletRequest request) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public String map(String resourcePath) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public String map(HttpServletRequest request, String resourcePath) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public Resource getResource(String path) {
+ Session session;
+ try {
+ session = createSession();
+ session.getNode(path);
+ } catch (PathNotFoundException e) {
+ return null;
+ } catch (RepositoryException e) {
+ throw new RuntimeException("RepositoryException: " + e, e);
+ }
+ return new MockedResource(this, path, "nt:unstructured");
+ }
+
+ public Resource getResource(Resource base, String path) {
+ if (base.getPath().equals("/")) {
+ return getResource("/" + path);
+ } else {
+ return getResource(base.getPath() + "/" + path);
+ }
+ }
+
+ public String[] getSearchPath() {
+ return SEARCH_PATHS;
+ }
+
+ public Iterator<Resource> listChildren(Resource parent) {
+ try {
+ Node node = parent.adaptTo(Node.class);
+ final NodeIterator nodes = node.getNodes();
+ return new Iterator<Resource>() {
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Resource next() {
+ Node next = nodes.nextNode();
+ try {
+ return new MockedResource(MockedResourceResolver.this,
+ next.getPath(), "nt:unstructured");
+ } catch (RepositoryException e) {
+ throw new RuntimeException("RepositoryException: " + e,
+ e);
+ }
+ }
+
+ public boolean hasNext() {
+ return nodes.hasNext();
+ }
+ };
+ } catch (RepositoryException e) {
+ throw new RuntimeException("RepositoryException: " + e, e);
+ }
+ }
+
+ public Iterable<Resource> getChildren(Resource parent) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public Iterator<Resource> findResources(String query, String language) {
+ List<Resource> resources = new ArrayList<Resource>();
+ try {
+ NodeIterator iterator = session.getWorkspace().getQueryManager().createQuery(query, language).execute().getNodes();
+ while (iterator.hasNext()) {
+ Node n = iterator.nextNode();
+ Resource resource = new MockedResource(this, n);
+ resources.add(resource);
+ }
+ } catch (RepositoryException e) {
+ LOG.error("Unable to execute JCR query", e);
+ }
+ return resources.iterator();
+ }
+
+ public Iterator<Map<String, Object>> queryResources(String query,
+ String language) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public ResourceResolver clone(Map<String, Object> authenticationInfo)
+ throws LoginException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public boolean isLive() {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public void close() {
+ Iterator<MockedResource> it = resources.iterator();
+ while (it.hasNext()) {
+ MockedResource r = it.next();
+ r.close();
+ }
+ if (session != null) {
+ if (session.isLive()) {
+ session.logout();
+ }
+ session = null;
+ }
+ }
+
+ public void register(MockedResource mockedResource) {
+ resources.add(mockedResource);
+ }
+
+ public String getUserID() {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public Iterator<String> getAttributeNames() {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public Object getAttribute(String name) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public void delete(Resource resource) throws PersistenceException {
+ if (resources.contains(resource)) {
+ resources.remove(resource);
+ }
+ Node node = resource.adaptTo(Node.class);
+ try {
+ node.remove();
+ } catch (RepositoryException e) {
+ throw new PersistenceException("RepositoryException: "+e, e);
+ }
+ }
+
+ public Resource create(Resource parent, String name,
+ Map<String, Object> properties) throws PersistenceException {
+ final Node parentNode = parent.adaptTo(Node.class);
+ try {
+ final Node child;
+ if (properties!=null && properties.containsKey("jcr:primaryType")) {
+ child = parentNode.addNode(name, (String) properties.get("jcr:primaryType"));
+ } else {
+ child = parentNode.addNode(name);
+ }
+ if (properties!=null) {
+ final Iterator<Entry<String, Object>> it = properties.entrySet().iterator();
+ while(it.hasNext()) {
+ final Entry<String, Object> entry = it.next();
+ if (entry.getKey().equals("jcr:primaryType")) {
+ continue;
+ }
+ if (entry.getValue() instanceof String) {
+ child.setProperty(entry.getKey(), (String)entry.getValue());
+ } else if (entry.getValue() instanceof Boolean) {
+ child.setProperty(entry.getKey(), (Boolean)entry.getValue());
+ } else if (entry.getValue() instanceof String[]) {
+ child.setProperty(entry.getKey(), (String[]) entry.getValue());
+ } else {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+ }
+ }
+ return getResource(parent, name);
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void revert() {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public void commit() throws PersistenceException {
+ try {
+ this.session.save();
+ } catch (final RepositoryException re) {
+ throw new PersistenceException("Unable to commit changes.", re);
+ }
+ }
+
+ public boolean hasChanges() {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public String getParentResourceType(Resource resource) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getParentResourceType(String resourceType) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean isResourceType(Resource resource, String resourceType) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void refresh() {
+ // TODO Auto-generated method stub
+
+ }
+}
Added: sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/util/TrieTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/util/TrieTest.java?rev=1629907&view=auto
==============================================================================
--- sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/util/TrieTest.java (added)
+++ sling/trunk/contrib/validation/core/src/test/java/org/apache/sling/validation/impl/util/TrieTest.java Tue Oct 7 15:10:37 2014
@@ -0,0 +1,65 @@
+/*
+ * 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 org.apache.sling.validation.impl.util;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+public class TrieTest {
+
+ private Trie<Object> dictionary;
+
+ @Before
+ public void setUp() {
+ dictionary = new Trie<Object>();
+ dictionary.insert("/apps/example", "/apps/example");
+ dictionary.insert("/apps/examples/node/jcr:content", "/apps/examples/node/jcr:content");
+ dictionary.insert("/apps/examples/node/jcr:content/nodes", "/apps/examples/node/jcr:content/nodes");
+ }
+
+ @Test
+ public void testLongestMatchingKey() throws Exception {
+ TrieNode<Object> node;
+ node = dictionary.getElementForLongestMatchingKey("/apps/examples/node/jcr:content/nodes/1");
+ assertTrue("/apps/examples/node/jcr:content/nodes".equals(node.getValue()));
+
+ node = dictionary.getElementForLongestMatchingKey("/apps/example/node/jcr:content/nodes/1");
+ assertTrue("/apps/example".equals(node.getValue()));
+
+ node = dictionary.getElementForLongestMatchingKey("/libs");
+ assertTrue(node.getValue() == null);
+ }
+
+ @Test
+ public void testExactKey() {
+ TrieNode<Object> node;
+
+ node = dictionary.getElement("/apps/examples/node/jcr:content/nodes");
+ assertTrue("/apps/examples/node/jcr:content/nodes".equals(node.getValue()));
+
+ node = dictionary.getElement("/apps/example");
+ assertTrue("/apps/example".equals(node.getValue()));
+
+ node = dictionary.getElement("/libs");
+ assertTrue(dictionary.ROOT.equals(node));
+ }
+
+}
Propchange: sling/trunk/contrib/validation/examples/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Oct 7 15:10:37 2014
@@ -0,0 +1,14 @@
+target
+bin
+*.iml
+*.ipr
+*.iws
+.settings
+.project
+.classpath
+.externalToolBuilders
+maven-eclipse.xml
+felix-cache
+sling-crankstart
+derby.log
+