Author: ernst Date: 2009-05-28 10:32:26 +0200 (Thu, 28 May 2009) New Revision: 35463
Added: speeltuin/ernst/mmbase-vob/pom.xml speeltuin/ernst/mmbase-vob/src/ speeltuin/ernst/mmbase-vob/src/main/ speeltuin/ernst/mmbase-vob/src/main/java/ speeltuin/ernst/mmbase-vob/src/main/java/nl/ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/DefaultQueryHelper.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Direction.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/EntityFilter.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Populator.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryDirection.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryHelper.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Embedded.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Entity.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Field.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/PosRel.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Rel.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/EpochDateConverter.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/FieldConverter.java speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/PassThroughFieldConverter.java speeltuin/ernst/mmbase-vob/src/main/resources/ speeltuin/ernst/mmbase-vob/src/main/resources/org/ speeltuin/ernst/mmbase-vob/src/main/resources/org/mmbase/ speeltuin/ernst/mmbase-vob/src/main/resources/org/mmbase/config/ speeltuin/ernst/mmbase-vob/src/test/ speeltuin/ernst/mmbase-vob/src/test/java/ speeltuin/ernst/mmbase-vob/src/test/java/nl/ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Image.java speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/NewsItem.java speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Tag.java speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/converters/ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/converters/ToLowercaseConverter.java speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/NoResultsQueryHelper.java speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PathFragmentToNodenrQueryHelper.java speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PopulatorTest.java speeltuin/ernst/mmbase-vob/src/test/resources/ speeltuin/ernst/mmbase-vob/src/test/resources/log4j.properties Modified: speeltuin/ernst/mmbase-vob/ Log: mmbase-vob in de speeltuin Property changes on: speeltuin/ernst/mmbase-vob ___________________________________________________________________ Name: svn:ignore + .classpath .project .settings target Added: speeltuin/ernst/mmbase-vob/pom.xml =================================================================== --- speeltuin/ernst/mmbase-vob/pom.xml (rev 0) +++ speeltuin/ernst/mmbase-vob/pom.xml 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>nl.vpro</groupId> + <artifactId>vpro-parent</artifactId> + <version>R20090226</version> + </parent> + + <groupId>nl.vpro</groupId> + <artifactId>mmbase-vob</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>mmbase-vob</name> + + <dependencies> + <dependency> + <groupId>nl.vpro.mmbase</groupId> + <artifactId>mmvpro</artifactId> + <version>1.8.7-SNAPSHOT</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + <version>2.5.6</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-jdbc</artifactId> + <version>2.5.6</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + <version>1.8.0</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.5</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymockclassextension</artifactId> + <version>2.4</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.4</version> + </dependency> + </dependencies> + <build> + <defaultGoal>install</defaultGoal> + </build> +</project> Property changes on: speeltuin/ernst/mmbase-vob/pom.xml ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/DefaultQueryHelper.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/DefaultQueryHelper.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/DefaultQueryHelper.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,58 @@ +package nl.vpro.mmbase.vob; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; + +import org.mmbase.bridge.Cloud; +import org.mmbase.bridge.Node; +import org.mmbase.bridge.NodeIterator; +import org.mmbase.bridge.NodeList; +import org.mmbase.bridge.Query; +import org.mmbase.bridge.util.Queries; + +/** + * Default implementation, almost one-to-one patch trough to mmbase with some + * minor enhancements, like explicit typing for sorting and query directions. + * + * @author [email protected] + */ +public class DefaultQueryHelper implements QueryHelper { + private static final EnumMap<Direction, String> remapSortDirection = new EnumMap<Direction, String>(Direction.class); + static { + remapSortDirection.put(Direction.ASC, "up"); + remapSortDirection.put(Direction.DESC, "down"); + } + + /* (non-Javadoc) + * @see nl.vpro.mmbase.vob.QueryHelper#query(org.mmbase.bridge.Cloud, int, java.lang.String, java.lang.String, java.lang.String, nl.vpro.mmbase.vob.Direction, nl.vpro.mmbase.vob.QueryDirection) + */ + public List<Node> query(Cloud cloud, int startNumber, String path, String fields, String sortField, Direction dir, QueryDirection queryDir) { + String queryDirectionAsString = queryDir.toString().toLowerCase(); + Query query = Queries.createQuery(cloud, String.valueOf(startNumber), path, fields, null, sortField, remapSortDirection.get(dir), queryDirectionAsString, true); + + return executeQuery(cloud, query); + } + + /* (non-Javadoc) + * @see nl.vpro.mmbase.vob.QueryHelper#query(org.mmbase.bridge.Cloud, int, java.lang.String, java.lang.String, java.lang.String, nl.vpro.mmbase.vob.Direction, nl.vpro.mmbase.vob.QueryDirection, int) + */ + public List<Node> query(Cloud cloud, int startNumber, String path, String fields, String sortField, Direction dir, QueryDirection queryDir, int max) { + String queryDirectionAsString = queryDir.toString().toLowerCase(); + + Query query = Queries.createQuery(cloud, String.valueOf(startNumber), path, fields, null, sortField, remapSortDirection.get(dir), queryDirectionAsString, true); + query.setMaxNumber(max); + + return executeQuery(cloud, query); + } + + private List<Node> executeQuery(Cloud cloud, Query query) { + List<Node> nodes = new ArrayList<Node>(); + NodeList list = cloud.getList(query); + NodeIterator nodeIterator = list.nodeIterator(); + while (nodeIterator.hasNext()) { + nodes.add(nodeIterator.nextNode()); + } + return nodes; + } +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/DefaultQueryHelper.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Direction.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Direction.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Direction.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,10 @@ +package nl.vpro.mmbase.vob; + +/** + * Defines possible directions to sort. + * + * @author [email protected] + */ +public enum Direction { + ASC, DESC +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Direction.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/EntityFilter.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/EntityFilter.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/EntityFilter.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,48 @@ +package nl.vpro.mmbase.vob; + +import java.io.IOException; +import java.util.concurrent.ConcurrentMap; + +import nl.vpro.mmbase.vob.annotations.Entity; + +import org.apache.log4j.Logger; +import org.springframework.core.type.ClassMetadata; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; + +/** + * Classpath scanner, retrieves entities from the classpath. Uses Springs' + * annotationscanner to do so. + * + * @author [email protected] + */ +public final class EntityFilter implements TypeFilter { + private static final Logger log = Logger.getLogger(EntityFilter.class); + private final ConcurrentMap<String, Class<?>> entityRegistry; + + public EntityFilter(final ConcurrentMap<String, Class<?>> entityRegistry) { + this.entityRegistry = entityRegistry; + } + + public boolean match(final MetadataReader metadataReader, final MetadataReaderFactory metadataReaderFactory) throws IOException { + final boolean matches = isEntity(metadataReader); + if (matches) { + final ClassMetadata classMetadata = metadataReader.getClassMetadata(); + if (classMetadata.isConcrete()) { + try { + Class<?> clazz = getClass().getClassLoader().loadClass(classMetadata.getClassName()); + final String builder = Populator.determineBuilder(clazz); + entityRegistry.putIfAbsent(builder, clazz); + } catch (final ClassNotFoundException e) { + log.warn("unable to load class: " + classMetadata.getClassName(), e); + } + } + } + return matches; + } + + private boolean isEntity(final MetadataReader metadataReader) { + return metadataReader.getAnnotationMetadata().hasAnnotation(Entity.class.getName()); + } +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/EntityFilter.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Populator.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Populator.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Populator.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,363 @@ +package nl.vpro.mmbase.vob; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import nl.vpro.mmbase.vob.annotations.Embedded; +import nl.vpro.mmbase.vob.annotations.Entity; +import nl.vpro.mmbase.vob.annotations.PosRel; +import nl.vpro.mmbase.vob.annotations.Rel; +import nl.vpro.mmbase.vob.converters.FieldConverter; + +import org.apache.commons.lang.StringUtils; +import org.mmbase.bridge.Cloud; +import org.mmbase.bridge.Node; +import org.mmbase.bridge.util.NodeMap; +import org.mmbase.util.logging.Logger; +import org.mmbase.util.logging.Logging; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.support.GenericApplicationContext; + +/** + * Entry class for converting mmbase nodes to annotated entity objects scanned + * from the classpath. + * + * NOTE: Apart from the entity registry all methods in this class are stateless + * since graphs are populated in a recursive manner. + * + * @author [email protected] + */ +public class Populator { + private static Logger log = Logging.getLoggerInstance(Populator.class.getName()); + + private static ConcurrentMap<String, Class<?>> entityRegistry = null; + private final QueryHelper queryHelper; + + /** + * Instantiates a DefaultQueryHelper to retrieve associations. + * + * @param pathToScan The base path the scan for entities + */ + public Populator(final String pathToScan) { + this(new DefaultQueryHelper(), pathToScan); + } + + /** + * @param queryHelper The query helper to use when executing subqueries. + * @param pathToScan The base path the scan for entities + */ + public Populator(final QueryHelper queryHelper, final String pathToScan) { + this.queryHelper = queryHelper; + scanClasspathForEntities(pathToScan); + } + + /** + * Searches the given path for object annotated with the @entity annotation. + * + * When found, entities are added to a registry to be used for quick lookups + * will converting. + * + * @param pathToScan The base path the scan for entities + */ + private void scanClasspathForEntities(final String pathToScan) { + if (entityRegistry == null) { + entityRegistry = new ConcurrentHashMap<String, Class<?>>(); + final GenericApplicationContext context = new GenericApplicationContext(); + final ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context); + scanner.resetFilters(false); + scanner.addIncludeFilter(new EntityFilter(entityRegistry)); + scanner.scan(pathToScan); + log.service(entityRegistry.size() + " Entity mappings scanned on classpath " + pathToScan); + } + } + + /** + * Call this method to convert a node to a graph of corresponding objects, + * including all annotated associations etc. This method will be called + * recursively and is stateless, as are the methods called from here. + * + * + * TODO: do we really need to provide the builderName ourselves? currently + * in here to simplify testing. Fix this, before it becomes a feature!! + * + * @param node The concrete node which should be unmarshalled + * @param builderName The name of the builder + * @return The populated object graph + */ + public Object unmarshallNode(final Node node, final String builderName) { + Object target = null; + if (hasEntityFor(builderName)) { + try { + target = entityRegistry.get(builderName).newInstance(); + copyBasicProperties(target, node); + copyEmbeddeds(target, node); + copyAssociations(target, node); + + } catch (final Exception e) { + log.error("unable to unmarshall node " + node); + } + } else { + log.debug("no entity mapping for builder " + builderName); + } + return target; + } + + private void copyBasicProperties(final Object target, final Node node) { + final NodeMap nodeMap = new NodeMap(node); + final Set<Map.Entry<String, Object>> fieldNameSet = uncheckedCast(nodeMap.entrySet()); + for (final Map.Entry<String, Object> entry : fieldNameSet) { + final String fieldName = entry.getKey(); + // final Object fieldValue = entry.getValue(); + populateField(target, fieldName, node); + } + } + + private void copyEmbeddeds(final Object target, final Node node) { + for (final Field field : target.getClass().getDeclaredFields()) { + field.setAccessible(true); + final Embedded embeddedAnnotation = field.getAnnotation(Embedded.class); + if (embeddedAnnotation != null) { + final String srcBuilder = determineBuilder(target.getClass()); + + try { + final Object result = retrieveEmbedded(node, srcBuilder, embeddedAnnotation); + field.set(target, result); + } catch (final Exception e) { + log.warn("unable to populate embedded field: "+e.getMessage()); + } + } + } + } + + private Object retrieveEmbedded(final Node node, final String srcBuilder, final Embedded embeddedAnnotation) { + final Cloud cloud = node.getCloud(); + final String targetBuilder = embeddedAnnotation.builder(); + final String relationRole = embeddedAnnotation.relationRole(); + final String path = String.format("%s,%s,%s", srcBuilder, relationRole, targetBuilder); + + final String targetField = String.format("%s.%s", targetBuilder, embeddedAnnotation.field()); + //TODO: parhaps the order field is a relation field? + final String orderField = String.format("%s.%s", targetBuilder, embeddedAnnotation.orderField()); + + final List<Node> virtualNodes = queryHelper.query(cloud, node.getNumber(), path, targetField, orderField, + embeddedAnnotation.orderDirection(), embeddedAnnotation.queryDirection(), 1); + + if (virtualNodes.size() > 0) { + try { + final FieldConverter fieldConvertor = embeddedAnnotation.convertor().newInstance(); + return fieldConvertor.convert(virtualNodes.get(0), targetField); + } catch (final Exception e) { + log.warn("unable to convert returntype"); + } + } + + return null; + } + + private void copyAssociations(final Object target, final Node node) { + for (final Field field : target.getClass().getDeclaredFields()) { + field.setAccessible(true); + final Rel relAnnotation = field.getAnnotation(Rel.class); + final PosRel posrelAnnotation = field.getAnnotation(PosRel.class); + + if (posrelAnnotation != null) { + final Class<?> relatedType = retrieveActualType(field); + final String srcBuilder = determineBuilder(target.getClass()); + final String targetBuilder = determineBuilder(relatedType); + final List<?> retrievedPosRel = retrievePosRel(node, relatedType, srcBuilder, targetBuilder, + posrelAnnotation); + safeSet(target, field, retrievedPosRel); + } else if (relAnnotation != null) { + final Class<?> relatedType = retrieveActualType(field); + final String srcBuilder = determineBuilder(target.getClass()); + final String targetBuilder = determineBuilder(relatedType); + final List<?> retrievedRel = retrieveRel(node, relatedType, srcBuilder, targetBuilder, relAnnotation); + safeSet(target, field, retrievedRel); + } + } + } + + private static void safeSet(final Object target, final Field field, final Object retrievedPosRel) { + field.setAccessible(true); + try { + field.set(target, retrievedPosRel); + } catch (final Exception e) { + log.error("unable to set target field " + field.getName() + " on object " + target); + } + } + + private <T> List<T> retrievePosRel(final Node node, final Class<T> relatedType, final String srcBuilder, + final String targetBuilder, final PosRel posRel) { + final Cloud cloud = node.getCloud(); + + final String path = String.format("%s,posrel,%s", srcBuilder, targetBuilder); + final String targetField = String.format("%s.number", targetBuilder); + final String orderField = "posrel.pos"; + + final List<Node> virtualNodes = queryHelper.query(cloud, node.getNumber(), path, targetField, orderField, + posRel.orderDirection(), posRel.queryDirection()); + final List<Node> nodes = loadNodes(cloud, virtualNodes, targetField); + return unmarshallList(relatedType, nodes); + } + + private <T> List<T> retrieveRel(final Node node, final Class<T> relatedType, final String srcBuilder, + final String targetBuilder, final Rel rel) { + final Cloud cloud = node.getCloud(); + + final String path = String.format("%s,%s", srcBuilder, targetBuilder); + final String targetField = String.format("%s.number", targetBuilder); + final String orderField = String.format("%s.%s", targetBuilder, rel.orderField()); + + final List<Node> virtualNodes = queryHelper.query(cloud, node.getNumber(), path, targetField, orderField, rel + .orderDirection(), rel.queryDirection()); + final List<Node> nodes = loadNodes(cloud, virtualNodes, targetField); + return unmarshallList(relatedType, nodes); + } + + private List<Node> loadNodes(final Cloud cloud, final List<Node> virtual, final String numberField) { + final List<Node> results = new ArrayList<Node>(virtual.size()); + for (final Node virtualNode : virtual) { + final Node realNode = cloud.getNode(virtualNode.getIntValue(numberField)); + results.add(realNode); + } + + return results; + } + + @SuppressWarnings("unchecked") + private <T> List<T> unmarshallList(final Class<T> relatedType, final List<Node> nodes) { + final List<T> results = new ArrayList<T>(nodes.size()); + for (final Node n : nodes) { + try { + final T relatedObject = (T) unmarshallNode(n, determineBuilder(relatedType)); + results.add(relatedObject); + } catch (final Exception e) { + log.warn("unable to instantiate related object of type " + relatedType + + " , no accessible contructor present?"); + } + } + return results; + } + + private static Class<?> retrieveActualType(final Field field) { + return (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; + } + + public static Class<?> determineParametrizedType(final Type type) { + if (type instanceof ParameterizedType) { + final ParameterizedType paramType = (ParameterizedType) type; + final Type[] typeArguments = paramType.getActualTypeArguments(); + if (typeArguments.length > 0) { + return (Class<?>) typeArguments[0]; + } + } + return null; + } + + public static String determineBuilder(final Class<?> clazz) { + return clazz.getAnnotation(Entity.class) != null ? clazz.getAnnotation(Entity.class).builder() : null; + } + + /** + * Set the value of an Entity property based on a node field value. this is + * done when a property of matching name is found in the Entity object, or + * when there is an entity property with a Field annotation that maps that + * property to this node field. If a Convertor is set through a Field + * annotation, it is applied before setting the property. + * + * @param target is the Entity object the field value is set on. + * @param fieldName name of node field that is being set. + * @param node that the value is copied from + */ + static void populateField(final Object target, final String fieldName, final Node node) { + final Field[] fields = target.getClass().getDeclaredFields(); + try { + for (final Field field : fields) { + final nl.vpro.mmbase.vob.annotations.Field fieldAnnotation = field + .getAnnotation(nl.vpro.mmbase.vob.annotations.Field.class); + if (fieldMatchesWithoutAnnotatedFieldMapping(fieldName, field, fieldAnnotation) + || AnnotatedFieldMappingMatchesNodeField(fieldName, fieldAnnotation)) { + setFieldValue(target, fieldName, node, field, fieldAnnotation); + return; + } + } + } catch (final Exception e) { + log.warn(String.format("unable to set property %s on object %s", fieldName, target)); + } + } + + private static boolean AnnotatedFieldMappingMatchesNodeField(final String fieldName, + final nl.vpro.mmbase.vob.annotations.Field fieldAnnotation) { + return fieldAnnotation != null && fieldAnnotation.nodeField().equalsIgnoreCase(fieldName); + } + + private static boolean fieldMatchesWithoutAnnotatedFieldMapping(final String fieldName, final Field field, + final nl.vpro.mmbase.vob.annotations.Field fieldAnnotation) { + return field.getName().equalsIgnoreCase(fieldName) + && (fieldAnnotation == null || "".equals(fieldAnnotation.nodeField())); + } + + private static void setFieldValue(final Object target, final String fieldName, final Node node, final Field field, + final nl.vpro.mmbase.vob.annotations.Field fieldAnnotation) throws IllegalAccessException, + InstantiationException { + field.setAccessible(true); + if (fieldAnnotation != null) { + field.set(target, fieldAnnotation.convertor().newInstance().convert(node, fieldName)); + } else { + field.set(target, node.getObjectValue(fieldName)); + } + } + + /** + * @param builderName + * @return true if There is an entity mapping for given node type. + */ + public boolean hasEntityFor(final String builderName) { + return entityRegistry.containsKey(builderName); + } + + /** + * @param builderName + * @return an entity class mapped to this builder + */ + public Class<?> getEntityFor(final String builderName) { + return entityRegistry.get(builderName); + } + + public Class<?> findEntityThatEmbeds(String relationRole, String builder) { + for (Class<?> entityClass : entityRegistry.values()) { + for (Field field : entityClass.getDeclaredFields()) { + Embedded embedded = field.getAnnotation(Embedded.class); + if (embedded != null) { + if (equalsNotBlank(relationRole, embedded.relationRole()) + && equalsNotBlank(builder, embedded.builder())) { + return entityClass; + } + } + } + } + return null; + } + + public static boolean entityIsRoot(Object o){ + Class<? extends Object> c = o.getClass(); + Entity e = (Entity) c.getAnnotation(Entity.class); + return e != null && e.root() == true; + } + + @SuppressWarnings("unchecked") + private static <T> T uncheckedCast(final Object obj) { + return (T) obj; + } + + private static boolean equalsNotBlank(String s1, String s2) { + return !StringUtils.isBlank(s1) && s1.equals(s2); + } +} \ No newline at end of file Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/Populator.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryDirection.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryDirection.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryDirection.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,10 @@ +package nl.vpro.mmbase.vob; + +/** + * Specify the directionality of the query. + * + * @author [email protected] + */ +public enum QueryDirection { + SOURCE, DESTINATION, BOTH +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryDirection.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryHelper.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryHelper.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryHelper.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,54 @@ +package nl.vpro.mmbase.vob; + +import java.util.List; + +import org.mmbase.bridge.Cloud; +import org.mmbase.bridge.Node; + +/** + * Wrapper interface for the Query utility in mmbase, to allow for mocking and + * alternative implementations. + * + * @author [email protected] + */ +interface QueryHelper { + /** + * @param cloud + * The cloud to query + * @param startNumber + * The startnode of the path + * @param path + * The actual path + * @param fields + * The fields to retrieve + * @param sortField + * The field to sort by + * @param dir + * The direction to sort in + * @param queryDir + * The direction to query in + * @return A list of resulting nodes + */ + List<Node> query(Cloud cloud, int startNumber, String path, String fields, String sortField, Direction dir, QueryDirection queryDir); + + /** + * @param cloud + * The cloud to query + * @param startNumber + * The startnode of the path + * @param path + * The actual path + * @param fields + * The fields to retrieve + * @param sortField + * The field to sort by + * @param dir + * The direction to sort in + * @param queryDir + * The direction to query in + * @param max + * The maximum number of nodes to retrieve + * @return A list of resulting nodes + */ + List<Node> query(Cloud cloud, int startNumber, String path, String fields, String sortField, Direction dir, QueryDirection queryDir, int max); +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/QueryHelper.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Embedded.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Embedded.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Embedded.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,24 @@ +package nl.vpro.mmbase.vob.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import nl.vpro.mmbase.vob.Direction; +import nl.vpro.mmbase.vob.QueryDirection; +import nl.vpro.mmbase.vob.converters.FieldConverter; +import nl.vpro.mmbase.vob.converters.PassThroughFieldConverter; + + +...@retention(RetentionPolicy.RUNTIME) +...@target(ElementType.FIELD) +public @interface Embedded { + String builder(); + String field(); + String relationRole() default "related"; + String orderField() default "number"; + Direction orderDirection() default Direction.ASC; + QueryDirection queryDirection() default QueryDirection.DESTINATION; + Class<? extends FieldConverter> convertor() default PassThroughFieldConverter.class; +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Embedded.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Entity.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Entity.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Entity.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,13 @@ +package nl.vpro.mmbase.vob.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +...@retention(RetentionPolicy.RUNTIME) +...@target(ElementType.TYPE) +public @interface Entity { + String builder(); + boolean root() default false; +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Entity.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Field.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Field.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Field.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,16 @@ +package nl.vpro.mmbase.vob.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import nl.vpro.mmbase.vob.converters.FieldConverter; +import nl.vpro.mmbase.vob.converters.PassThroughFieldConverter; + +...@retention(RetentionPolicy.RUNTIME) +...@target(ElementType.FIELD) +public @interface Field { + String nodeField() default ""; + Class<? extends FieldConverter> convertor() default PassThroughFieldConverter.class; +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Field.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/PosRel.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/PosRel.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/PosRel.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,19 @@ +package nl.vpro.mmbase.vob.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import nl.vpro.mmbase.vob.Direction; +import nl.vpro.mmbase.vob.QueryDirection; + + +...@retention(RetentionPolicy.RUNTIME) +...@target(ElementType.FIELD) +...@rel +public @interface PosRel { + Direction orderDirection() default Direction.ASC; + + QueryDirection queryDirection() default QueryDirection.DESTINATION; +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/PosRel.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Rel.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Rel.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Rel.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,20 @@ +package nl.vpro.mmbase.vob.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import nl.vpro.mmbase.vob.Direction; +import nl.vpro.mmbase.vob.QueryDirection; + + +...@retention(RetentionPolicy.RUNTIME) +...@target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +public @interface Rel { + String orderField() default "number"; + + Direction orderDirection() default Direction.ASC; + + QueryDirection queryDirection() default QueryDirection.DESTINATION; +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/annotations/Rel.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/EpochDateConverter.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/EpochDateConverter.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/EpochDateConverter.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,23 @@ +package nl.vpro.mmbase.vob.converters; + +import java.util.Date; + +import nl.vpro.mmbase.vob.converters.FieldConverter; + +import org.mmbase.bridge.Node; + +/** + * Converts 'seconds from the epoch' to a real date object. + * + * @author [email protected] + */ +public class EpochDateConverter implements FieldConverter { + + /* (non-Javadoc) + * @see nl.vpro.mmbase.vob.convertors.FieldConvertor#convert(org.mmbase.bridge.Node, java.lang.String) + */ + public Object convert(Node node, String field) { + return new Date(node.getLongValue(field) * 1000L); + } + +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/EpochDateConverter.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/FieldConverter.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/FieldConverter.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/FieldConverter.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,15 @@ +package nl.vpro.mmbase.vob.converters; + +/** + * Convertors are used to transform the mmbase fieldvalue to the wanted type. + * + * @author [email protected] + */ +public interface FieldConverter { + /** + * @param node The node containing the field + * @param field The name of the field to convert + * @return the resulting value + */ + Object convert(org.mmbase.bridge.Node node, String field); +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/FieldConverter.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/PassThroughFieldConverter.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/PassThroughFieldConverter.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/PassThroughFieldConverter.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,11 @@ +package nl.vpro.mmbase.vob.converters; + +import org.mmbase.bridge.Node; + +public class PassThroughFieldConverter implements FieldConverter { + + public Object convert(Node node, String field) { + return node.getObjectValue(field); + } + +} Property changes on: speeltuin/ernst/mmbase-vob/src/main/java/nl/vpro/mmbase/vob/converters/PassThroughFieldConverter.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Image.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Image.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Image.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,34 @@ +package nl.vpro.mmbase; + +import nl.vpro.mmbase.vob.annotations.Entity; + +...@entity(builder = "images") +public class Image { + private Long number; + private String title; + + public Long getNumber() { + return number; + } + + public void setNumber(Long number) { + this.number = number; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getUrl() { + return String.format("http://images.vpro.nl/%d", number); + } + + @Override + public String toString() { + return String.format("Image[%d, %s]", number, title); + } +} Property changes on: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Image.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/NewsItem.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/NewsItem.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/NewsItem.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,117 @@ +package nl.vpro.mmbase; + +import java.util.Date; +import java.util.List; + +import nl.vpro.mmbase.converters.ToLowercaseConverter; +import nl.vpro.mmbase.vob.Direction; +import nl.vpro.mmbase.vob.QueryDirection; +import nl.vpro.mmbase.vob.annotations.Embedded; +import nl.vpro.mmbase.vob.annotations.Entity; +import nl.vpro.mmbase.vob.annotations.Field; +import nl.vpro.mmbase.vob.annotations.PosRel; +import nl.vpro.mmbase.vob.annotations.Rel; +import nl.vpro.mmbase.vob.converters.EpochDateConverter; + +...@entity(builder = "news", root=true) +public class NewsItem { + + private Long number; + private String title; + + @Field(convertor = ToLowercaseConverter.class) + private String subtitle; + + private String credits; + + @Field(nodeField = "intro") + private String description; + + private String body; + + @Embedded(builder = "mmevents", relationRole="posrel", queryDirection = QueryDirection.BOTH, field = "start", convertor = EpochDateConverter.class) + private Date created; + + @PosRel(orderDirection = Direction.DESC, queryDirection = QueryDirection.SOURCE) + private List<Image> images; + + @Rel(orderDirection = Direction.DESC, orderField = "value", queryDirection = QueryDirection.SOURCE) + private List<Tag> tags; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getNumber() { + return number; + } + + public void setNumber(Long number) { + this.number = number; + } + + public String getSubtitle() { + return subtitle; + } + + public void setSubtitle(String subtitle) { + this.subtitle = subtitle; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getCredits() { + return credits; + } + + public void setCredits(String credits) { + this.credits = credits; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public List<Image> getImages() { + return images; + } + + public void setImages(List<Image> images) { + this.images = images; + } + + public List<Tag> getTags() { + return tags; + } + + public void setTags(List<Tag> tags) { + this.tags = tags; + } + + @Override + public String toString() { + return String.format("News[%d, %s]", number, title); + } +} Property changes on: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/NewsItem.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Tag.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Tag.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Tag.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,30 @@ +package nl.vpro.mmbase; + +import nl.vpro.mmbase.vob.annotations.Entity; + +...@entity(builder = "tags") +public class Tag { + private Long number; + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public Long getNumber() { + return number; + } + + public void setNumber(Long number) { + this.number = number; + } + + @Override + public String toString() { + return String.format("Tag[%d, %s]", number, value); + } +} Property changes on: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/Tag.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/converters/ToLowercaseConverter.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/converters/ToLowercaseConverter.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/converters/ToLowercaseConverter.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,13 @@ +package nl.vpro.mmbase.converters; + +import org.mmbase.bridge.Node; + +import nl.vpro.mmbase.vob.converters.FieldConverter; + +public class ToLowercaseConverter implements FieldConverter { + + public Object convert(Node node, String field) { + return node.getStringValue(field).toLowerCase(); + } + +} Property changes on: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/converters/ToLowercaseConverter.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/NoResultsQueryHelper.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/NoResultsQueryHelper.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/NoResultsQueryHelper.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,21 @@ +package nl.vpro.mmbase.vob; + +import java.util.Collections; +import java.util.List; + +import org.mmbase.bridge.Cloud; +import org.mmbase.bridge.Node; + +public class NoResultsQueryHelper implements QueryHelper { + + public List<Node> query(Cloud cloud, int startNumber, String path, String fields, String sortField, + Direction dir, QueryDirection queryDir) { + return Collections.emptyList(); + } + + public List<Node> query(Cloud cloud, int startNumber, String path, String fields, String sortField, + Direction dir, QueryDirection queryDir, int max) { + return Collections.emptyList(); + } + +} Property changes on: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/NoResultsQueryHelper.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PathFragmentToNodenrQueryHelper.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PathFragmentToNodenrQueryHelper.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PathFragmentToNodenrQueryHelper.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,43 @@ +package nl.vpro.mmbase.vob; + +import java.util.*; + +import org.mmbase.bridge.Cloud; +import org.mmbase.bridge.Node; +import org.mmbase.bridge.util.MapNode; + +public class PathFragmentToNodenrQueryHelper extends NoResultsQueryHelper{ + private String path; + Map<String, ? extends Object> map; + + public PathFragmentToNodenrQueryHelper(String nodeType, int nodenrToReturn) { + path = nodeType; + this.map = Collections.singletonMap(nodeType + ".number", new Integer(nodenrToReturn)); + } + + public PathFragmentToNodenrQueryHelper(String nodeType, int nodenrToReturn, String relationRole) { + path = relationRole + "," + nodeType; + this.map = Collections.singletonMap(nodeType + ".number", new Integer(nodenrToReturn)); + } + + public PathFragmentToNodenrQueryHelper(String nodeType, String relationRole, Map<String, ? extends Object> returnValuesMap) { + this.path = relationRole + "," + nodeType; + this.map = returnValuesMap; + } + + public List<Node> query(Cloud cloud, int startNumber, String path, String fields, String sortField, Direction dir, + QueryDirection queryDir) { + + if (path.contains(this.path)) { + MapNode dummyNode = new MapNode(map, cloud); + return Collections.singletonList((Node) dummyNode); + } else { + return Collections.emptyList(); + } + } + + public List<Node> query(Cloud cloud, int startNumber, String path, String fields, String sortField, Direction dir, + QueryDirection queryDir, int max) { + return query(cloud, startNumber, path, fields, sortField, dir, queryDir).subList(0, max); + } +} Property changes on: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PathFragmentToNodenrQueryHelper.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PopulatorTest.java =================================================================== --- speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PopulatorTest.java (rev 0) +++ speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PopulatorTest.java 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,170 @@ +package nl.vpro.mmbase.vob; + +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; + +import java.beans.FeatureDescriptor; +import java.util.*; + +import nl.vpro.mmbase.Image; +import nl.vpro.mmbase.NewsItem; + +import org.junit.Test; +import org.mmbase.bridge.Cloud; +import org.mmbase.bridge.Node; +import org.mmbase.bridge.util.MapNode; + +public class PopulatorTest { + + @Test + public void testUnmarshallNode() throws IllegalArgumentException, IllegalAccessException, SecurityException, + NoSuchFieldException { + Populator populator = new Populator(new NoResultsQueryHelper(), "nl.vpro"); + +// Map<String, Object> map = createDefaultNodeForNewsitem((Cloud) null); + Node newsItemNode = createDefaultNodeForNewsitem((Cloud) null); + + NewsItem item = (NewsItem) populator.unmarshallNode(newsItemNode, "news"); + + assertEquals(Long.valueOf(1234), item.getNumber()); + assertEquals("title", item.getTitle()); + } + + /** + * The field annotation can be used with just a converter + */ + @Test + public void testFieldConverterOnFieldAnnotation() throws IllegalArgumentException, IllegalAccessException, + SecurityException, NoSuchFieldException { + Populator populator = new Populator(new NoResultsQueryHelper(), "nl.vpro"); + + Map<String, Object> map = new Mapper().put("number", Long.valueOf(1234)).put("subtitle", "Subtitle").getMap(); + MapNode newsItemNode = new MapNode(map, (Cloud) null); + + NewsItem item = (NewsItem) populator.unmarshallNode(newsItemNode, "news"); + + assertEquals(Long.valueOf(1234), item.getNumber()); + assertEquals("subtitle", item.getSubtitle()); + } + + @Test + public void testUnmarshallNodeWithAssociations() throws IllegalArgumentException, IllegalAccessException, + SecurityException, NoSuchFieldException { + Cloud cloud = createMock(Cloud.class); + Map<String, Object> map = new Mapper().put("number", Long.valueOf(12345)).put("title", "image title").getMap(); + MapNode imageNode = new MapNode(map, (Cloud) null); + + expect(cloud.getNode(12345)).andReturn((Node) imageNode); + replay(cloud); + + Populator populator = new Populator(new PathFragmentToNodenrQueryHelper("images", 12345), "nl.vpro"); + map = new Mapper() + .put("number", Long.valueOf(1234)) + .put("title", "title").getMap(); + MapNode newsItemNode = new MapNode(map, cloud); + + NewsItem item = (NewsItem) populator.unmarshallNode(newsItemNode, "news"); + + assertEquals(Long.valueOf(1234), item.getNumber()); + assertEquals("title", item.getTitle()); + assertEquals(1, item.getImages().size()); + Image image = item.getImages().get(0); + assertEquals(new Long(12345), image.getNumber()); + assertEquals("image title", image.getTitle()); + verify(cloud); + } + + @Test + public void testEntityIsRoot(){ + Populator populator = new Populator(null, "nl.vpro"); + Node node = new MapNode(new Mapper() + .put("number", Long.valueOf(1234)) + .getMap(), (Cloud) null); + + assertTrue(Populator.entityIsRoot(populator.unmarshallNode(node, "news"))); + assertFalse(Populator.entityIsRoot(populator.unmarshallNode(node, "images"))); + } + + @Test + public void testUnmashallNodeWithEmbedded() { + + Map<String, Object> returnValuesMap = new Mapper() + .put("mmevents.number", Long.valueOf(12345)) + .put("mmevents.start", Long.valueOf(createMyBirthday().getTimeInMillis() / 1000L)) + .getMap(); + PathFragmentToNodenrQueryHelper queryHelper = new PathFragmentToNodenrQueryHelper("mmevents", "posrel", returnValuesMap); + Populator populator = new Populator(queryHelper, "nl.vpro"); + + Node createDefaultNodeForNewsitem = createDefaultNodeForNewsitem(null); + NewsItem item = (NewsItem) populator.unmarshallNode(createDefaultNodeForNewsitem, "news"); + + assertNotNull(item.getCreated()); + Calendar c = new GregorianCalendar(); + c.setTime(item.getCreated()); + assertEquals(1970, c.get(Calendar.YEAR)); + assertEquals(Calendar.FEBRUARY, c.get(Calendar.MONTH)); + assertEquals(19, c.get(Calendar.DAY_OF_MONTH)); + } + + + + /** + * + * @param cloud use mock cloud if this node is triggering a query + * @return + */ + private Node createDefaultNodeForNewsitem(Cloud cloud) { + Map<String,Object> map = new Mapper() + .put("number", Long.valueOf(1234)) + .put("title", "title").getMap(); + MapNode newsItemNode = new MapNode(map, cloud); + return newsItemNode; + } + + private Cloud createMockCloud(Map<String,Object> node, int nodenr){ + Cloud cloud = createMock(Cloud.class); + if(node != null){ + MapNode mmeventsNode = new MapNode(node, (Cloud) null); + expect(cloud.getNode(nodenr)).andReturn((Node) mmeventsNode); + } + replay(cloud); + return cloud; + } + + private Calendar createMyBirthday() { + Calendar c = new GregorianCalendar(); + c.set(1970, Calendar.FEBRUARY, 19); + return c; + } + + //@Test + public void testFindEntityThatEmbeds() { + Populator populator = new Populator(new PathFragmentToNodenrQueryHelper("mmevents", 12345, "posrel"), "nl.vpro"); + Class<?> c = populator.findEntityThatEmbeds("posrel", "mmevents"); + assertEquals(NewsItem.class, c); + + c = populator.findEntityThatEmbeds("related", "mmevents"); + assertNull(c); + } + + // @Test + public void thePopulatorCanDetermineBuildersFromEntities() { + assertEquals("images", Populator.determineBuilder(Image.class)); + assertEquals("news", Populator.determineBuilder(NewsItem.class)); + } + + + + public static class Mapper { + private Map<String, Object> wrapped = new HashMap<String, Object>(); + + public Mapper put(String key, Object value) { + wrapped.put(key, value); + return this; + } + + public Map<String, Object> getMap() { + return wrapped; + } + } +} Property changes on: speeltuin/ernst/mmbase-vob/src/test/java/nl/vpro/mmbase/vob/PopulatorTest.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: speeltuin/ernst/mmbase-vob/src/test/resources/log4j.properties =================================================================== --- speeltuin/ernst/mmbase-vob/src/test/resources/log4j.properties (rev 0) +++ speeltuin/ernst/mmbase-vob/src/test/resources/log4j.properties 2009-05-28 08:32:26 UTC (rev 35463) @@ -0,0 +1,12 @@ +//Example log4j Configuration File +/* +#set the level of the root logger to DEBUG and set its appender +as an appender named X +log4j.rootLogger = INFO, X + +#set the appender named X to be a console appender +log4j.appender.X=org.apache.log4j.ConsoleAppender + +#set the layout for the appender X +log4j.appender.X.layout=org.apache.log4j.PatternLayout +log4j.appender.X.layout.conversionPattern=%m%n Property changes on: speeltuin/ernst/mmbase-vob/src/test/resources/log4j.properties ___________________________________________________________________ Name: svn:mime-type + text/plain _______________________________________________ Cvs mailing list [email protected] http://lists.mmbase.org/mailman/listinfo/cvs
