/*
* Copyright (c) 2013 Simon Templer
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Simon Templer - initial version
*/
package eu.esdihumboldt.util.groovy.paths;
import groovy.lang.GroovyObjectSupport;
import groovy.lang.MissingMethodException;
import groovy.lang.MissingPropertyException;
import java.util.Collections;
import java.util.List;
import org.codehaus.groovy.runtime.InvokerHelper;
import com.google.common.collect.ImmutableList;
/**
* Base class for path accessors.
*
* It mutates, so an instance is only usable once.
*
* @param <C> the path element type
* @author Simon Templer
*/
public abstract class AbstractAccessor<C> extends GroovyObjectSupport {
/**
* The paths
*/
private List<? extends Path<C>> accessorPaths;
/**
* Creates a new accessor.
*
* @param parentPath the root path, usually containing only one path with
* the parent element
*/
public AbstractAccessor(Path<C> parentPath) {
this(ImmutableList.of(parentPath));
}
/**
* Creates a new accessor.
*
* @param initialPaths the initial paths, usually containing only one path
* with the parent element
*/
public AbstractAccessor(List<? extends Path<C>> initialPaths) {
this.accessorPaths = initialPaths;
}
@Override
public Object getProperty(String property) {
try {
return super.getProperty(property);
} catch (MissingPropertyException e) {
// missing property
return findChildren(property, Collections.EMPTY_LIST);
}
}
@Override
public Object invokeMethod(String name, Object args) {
try {
return super.invokeMethod(name, args);
} catch (MissingMethodException e) {
// missing method
return findChildren(name, InvokerHelper.asList(args));
}
}
/**
* Find children with the given name.
*
* @param name the property name
* @return this accessor
*/
public AbstractAccessor<C> findChildren(String name) {
return findChildren(name, Collections.EMPTY_LIST);
}
/**
* Find children with the given name.
*
* @param name the property name
* @param args the list of additional arguments apart from the name
* @return this accessor
*/
public AbstractAccessor<C> findChildren(String name, List<?> args) {
accessorPaths = findChildPaths(accessorPaths, name, args);
return this;
}
/**
* Find child paths for the given name.
*
* @param parentPaths the parent paths
* @param name the property name
* @param args the list of additional arguments apart from the name
* @return the list of sub paths replacing the parent paths
*/
protected abstract List<? extends Path<C>> findChildPaths(List<? extends Path<C>> parentPaths,
String name, List<?> args);
/**
* Get all found paths.
*
* @return the list of paths
*/
public List<? extends Path<C>> all() {
return accessorPaths;
}
/**
* Get a unique found child path.
*
* @return a child path or <code>null</code> if none was found
* @throws IllegalStateException if there are multiple paths
*/
public Path<C> eval() {
return eval(true);
}
/**
* Get a single found child path.
*
* @param unique if the path must be unique
* @return a child path or <code>null</code> if none was found
* @throws IllegalStateException if there are multiple paths but a unique
* path was requested
*/
public Path<C> eval(boolean unique) {
if (accessorPaths == null || accessorPaths.isEmpty()) {
return null;
}
else if (!unique || accessorPaths.size() == 1) {
// return a single property
return accessorPaths.get(0);
}
else {
throw new IllegalStateException("Multiple possible child paths found");
}
}
}