/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* 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:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.common.align.model.transformation.tree.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import net.jcip.annotations.Immutable;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import eu.esdihumboldt.hale.common.align.model.AlignmentUtil;
import eu.esdihumboldt.hale.common.align.model.ChildContext;
import eu.esdihumboldt.hale.common.align.model.Entity;
import eu.esdihumboldt.hale.common.align.model.EntityDefinition;
import eu.esdihumboldt.hale.common.align.model.transformation.tree.CellNode;
import eu.esdihumboldt.hale.common.align.model.transformation.tree.GroupNode;
import eu.esdihumboldt.hale.common.align.model.transformation.tree.TargetNode;
import eu.esdihumboldt.hale.common.align.model.transformation.tree.TransformationNode;
import eu.esdihumboldt.hale.common.align.model.transformation.tree.TransformationNodeVisitor;
import eu.esdihumboldt.hale.common.align.model.transformation.tree.TransformationTree;
import eu.esdihumboldt.hale.common.schema.model.ChildDefinition;
import eu.esdihumboldt.hale.common.schema.model.TypeDefinition;
/**
* Default {@link TargetNode} implementation
*
* @author Simon Templer
*/
@Immutable
public class TargetNodeImpl extends AbstractGroupNode implements TargetNode {
private final EntityDefinition entity;
private final SetMultimap<CellNode, String> assignments;
private final List<TargetNode> children;
/**
* Create a target node that is populated with assignments and children
* according to the given parameters.
*
* @param entity the associated definition
* @param cells the cells associated with this node or its children
* @param parentType the type representing the root
* @param depth the depth down from the root node
* @param parent the parent node
*/
public TargetNodeImpl(EntityDefinition entity, Collection<CellNode> cells,
TypeDefinition parentType, int depth, GroupNode parent) {
super(parent);
this.entity = entity;
// partition cells by child
ListMultimap<EntityDefinition, CellNode> childCells = ArrayListMultimap.create();
// ... and for this node
SetMultimap<CellNode, String> assignSet = HashMultimap.create();
for (CellNode cell : cells) {
for (Entry<String, ?> entry : cell.getCell().getTarget().asMap().entrySet()) {
String name = entry.getKey();
@SuppressWarnings("unchecked")
Collection<? extends Entity> entities = (Collection<? extends Entity>) entry
.getValue();
for (Entity target : entities) {
if (target.getDefinition().getType().equals(parentType)) {
List<ChildContext> path = target.getDefinition().getPropertyPath();
if (path.get(depth - 1).getChild().equals(entity.getDefinition())) {
if (path.size() <= depth) {
// this cell belongs to this node
assignSet.put(cell, name);
cell.addTarget(this);
}
else {
// this cell belongs to a child node
childCells.put(AlignmentUtil.deriveEntity(target.getDefinition(),
depth + 1), cell);
}
}
}
}
}
}
assignments = Multimaps.unmodifiableSetMultimap(assignSet);
// create child cells
List<TargetNode> childList = new ArrayList<TargetNode>();
for (Entry<EntityDefinition, Collection<CellNode>> childEntry : childCells.asMap()
.entrySet()) {
TargetNode childNode = new TargetNodeImpl(childEntry.getKey(), childEntry.getValue(),
parentType, depth + 1, this);
childList.add(childNode);
}
children = Collections.unmodifiableList(childList);
}
/**
* Create a target node associated with the given entity definition but
* unpopulated.
*
* @param entity the entity definition
* @param parent the parent node
*/
public TargetNodeImpl(EntityDefinition entity, GroupNode parent) {
super(parent);
this.entity = entity;
this.assignments = HashMultimap.create();
this.children = new ArrayList<TargetNode>();
}
/**
* Add an assignment to the target node. May only be called if the target
* node was created using the
* {@link #TargetNodeImpl(EntityDefinition, GroupNode)} constructor.
*
* @param names the entity names associated to the assignment
* @param cell the cell node representing the assignment
*/
public void addAssignment(Set<String> names, CellNode cell) {
assignments.putAll(cell, names);
}
/**
* Add a child to the target node. May only be called if the target node was
* created using the {@link #TargetNodeImpl(EntityDefinition, GroupNode)}
* constructor.
*
* @param node the node to add as child, this node will be set as its parent
*/
public void addChild(TargetNode node) {
children.add(node);
}
// /**
// * Set the parent node.
// * @param parent the parent node
// */
// public void setParent(GroupNode parent) {
// this.parent = parent;
// }
/**
* @see TransformationNode#accept(TransformationNodeVisitor)
*/
@Override
public void accept(TransformationNodeVisitor visitor) {
if (visitor.visit(this)) {
if (visitor.isFromTargetToSource()) {
// visit children
for (TargetNode child : getChildren(visitor.includeAnnotatedNodes())) {
child.accept(visitor);
}
// visit cells
for (CellNode cell : assignments.keySet()) {
cell.accept(visitor);
}
}
else {
// visit parent
if (getParent() != null) {
GroupNode parent = getParent();
if (parent instanceof TargetNode) {
((TargetNode) parent).accept(visitor);
}
else if (parent instanceof TransformationTree) {
((TransformationTree) parent).accept(visitor);
}
}
}
}
visitor.leave(this);
}
/**
* @see AbstractGroupNode#getFixedChildren()
*/
@Override
public List<TargetNode> getFixedChildren() {
return children;
}
/**
* @see TargetNode#getAssignments()
*/
@Override
public Set<CellNode> getAssignments() {
return assignments.keySet();
}
/**
* @see TargetNode#getAssignmentNames(CellNode)
*/
@Override
public Set<String> getAssignmentNames(CellNode assignment) {
return assignments.get(assignment);
}
/**
* @see TargetNode#getDefinition()
*/
@Override
public ChildDefinition<?> getDefinition() {
return (ChildDefinition<?>) entity.getDefinition();
}
/**
* @see TargetNode#getEntityDefinition()
*/
@Override
public EntityDefinition getEntityDefinition() {
return entity;
}
/**
* @see TargetNode#isDefined()
*/
@Override
public boolean isDefined() {
Object value = getAnnotation(ANNOTATION_RESULT_DEFINED);
if (value instanceof Boolean) {
return (Boolean) value;
}
return false;
}
/**
* @see TargetNode#setDefined(boolean)
*/
@Override
public void setDefined(boolean defined) {
setAnnotation(ANNOTATION_RESULT_DEFINED, defined);
}
/**
* @see TargetNode#getResult()
*/
@Override
public Object getResult() {
return getAnnotation(ANNOTATION_RESULT);
}
/**
* @see TargetNode#setResult(Object)
*/
@Override
public void setResult(Object value) {
setAnnotation(ANNOTATION_RESULT, value);
setDefined(true);
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((entity == null) ? 0 : entity.hashCode());
return result;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TargetNodeImpl other = (TargetNodeImpl) obj;
if (entity == null) {
if (other.entity != null)
return false;
}
else if (!entity.equals(other.entity))
return false;
return true;
}
}