Java tutorial
/* * #%L * Protempa Framework * %% * Copyright (C) 2012 - 2013 Emory University * %% * Licensed 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. * #L% */ package org.protempa.dest.table; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import org.apache.commons.lang3.builder.ToStringBuilder; import org.arp.javautil.arrays.Arrays; import org.protempa.KnowledgeSource; import org.protempa.KnowledgeSourceCache; import org.protempa.KnowledgeSourceReadException; import org.protempa.PropositionDefinition; import org.protempa.proposition.Parameter; import org.protempa.proposition.Proposition; import org.protempa.proposition.TemporalProposition; import org.protempa.proposition.UniqueId; import org.protempa.proposition.interval.Relation; import org.protempa.proposition.value.Value; /** * Traverses traversals from a proposition to/from a derived proposition. * * @author Andrew Post */ public final class Derivation extends Link { private static final Value[] EMPTY_VALUE_ARRAY = new Value[0]; /** * For configuring the direction of the traversal and how many derivation * steps to traverse. */ public static enum Behavior { /** * Forward chaining, toward derived propositions. */ SINGLE_FORWARD, /** * Backward chaining, toward raw data. */ SINGLE_BACKWARD, /** * Forward chaining, toward derived propositions. */ MULT_FORWARD, /** * Backward chaining, toward raw data. */ MULT_BACKWARD } private final Behavior behavior; private final Value[] allowedValues; private Set<String> knowledgeTree; private final Relation relation; private final Queue<Proposition> internalDerived; public Derivation(String[] propositionIds, Behavior behavior) { this(propositionIds, null, null, behavior, null); } public Derivation(String[] propositionIds, PropertyConstraint[] constraints, Behavior behavior) { this(propositionIds, constraints, null, -1, -1, null, behavior, null); } public Derivation(String[] propositionIds, PropertyConstraint[] constraints, Value[] allowedValues, Behavior behavior) throws KnowledgeSourceReadException { this(propositionIds, constraints, null, -1, -1, allowedValues, behavior, null); } public Derivation(String[] propositionIds, PropertyConstraint[] constraints, Value[] allowedValues, Behavior behavior, Relation relation) { this(propositionIds, constraints, null, -1, -1, allowedValues, behavior, relation); } public Derivation(String[] propositionIds, PropertyConstraint[] constraints, Comparator<Proposition> comparator, int index, Behavior behavior) throws KnowledgeSourceReadException { this(propositionIds, constraints, comparator, index, index >= 0 ? index + 1 : -1, null, behavior, null); } public Derivation(String[] propositionIds, PropertyConstraint[] constraints, Comparator<Proposition> comparator, int index, Value[] allowedValues, Behavior behavior) { this(propositionIds, constraints, comparator, index, index >= 0 ? index + 1 : -1, allowedValues, behavior, null); } public Derivation(String[] propositionIds, PropertyConstraint[] constraints, Comparator<Proposition> comparator, int fromIndex, int toIndex, Behavior behavior) throws KnowledgeSourceReadException { this(propositionIds, constraints, comparator, fromIndex, toIndex, null, behavior, null); } public Derivation(String[] propositionIds, PropertyConstraint[] constraints, Comparator<Proposition> comparator, int fromIndex, int toIndex, Value[] allowedValues, Behavior behavior, Relation relation) { super(propositionIds, constraints, comparator, fromIndex, toIndex); if (allowedValues == null) { this.allowedValues = EMPTY_VALUE_ARRAY; } else { this.allowedValues = allowedValues.clone(); } if (behavior == null) { throw new IllegalArgumentException("behavior cannot be null"); } this.behavior = behavior; this.relation = relation; this.internalDerived = new LinkedList<>(); } @Override public String[] getInferredPropositionIds(KnowledgeSource knowledgeSource, String[] inPropIds) throws KnowledgeSourceReadException { String[] explicitPropIds = getPropositionIds(); if (explicitPropIds.length > 0) { return explicitPropIds; } else { Set<String> result = new HashSet<>(); for (String propId : inPropIds) { PropositionDefinition propDef = knowledgeSource.readPropositionDefinition(propId); if (propDef == null) { throw new IllegalArgumentException("Invalid propId: " + propId); } switch (this.behavior) { case SINGLE_BACKWARD: Arrays.addAll(result, propDef.getChildren()); break; case MULT_BACKWARD: Queue<String> backwardProps = new LinkedList<>(); Arrays.addAll(backwardProps, propDef.getChildren()); String pId; while ((pId = backwardProps.poll()) != null) { PropositionDefinition pDef = knowledgeSource.readPropositionDefinition(pId); Arrays.addAll(backwardProps, pDef.getChildren()); } result.addAll(backwardProps); break; case SINGLE_FORWARD: for (PropositionDefinition def : knowledgeSource.readParents(propDef)) { result.add(def.getId()); } break; case MULT_FORWARD: Queue<String> forwardProps = new LinkedList<>(); for (PropositionDefinition def : knowledgeSource.readParents(propDef)) { forwardProps.add(def.getId()); } // pId is declared in MULT_BACKWARD case. while ((pId = forwardProps.poll()) != null) { PropositionDefinition pDef = knowledgeSource.readPropositionDefinition(pId); for (PropositionDefinition def : knowledgeSource.readParents(pDef)) { forwardProps.add(def.getId()); } } result.addAll(forwardProps); break; default: throw new AssertionError("Invalid derivation behavior specified"); } } return result.toArray(new String[result.size()]); } } @Override String headerFragment() { return createHeaderFragment("derived"); } /** * Traverses a derivation. * * @param proposition a {@link Proposition} at which to start the traversal. * @param forwardDerivations a {@link Map<Proposition,List<Proposition>>} of * derived propositions. * @param backwardDerivations a {@link Map<Proposition,List<Proposition>>} * of derived propositions. * @param references a {@link Map<Proposition,Proposition>} of unique * identifiers to {@link Proposition}s, used to resolve references. * @param knowledgeSource the {@link KnowledgeSource}. * @param cache a {@link Set<Proposition>} for convenience in checking if * duplicate propositions are traversed to. It is cleared in between calls * to this method. * @return the {@link Collection<Proposition>} at the end of the traversal * step. */ @Override Collection<Proposition> traverse(Proposition proposition, Map<Proposition, List<Proposition>> forwardDerivations, Map<Proposition, List<Proposition>> backwardDerivations, Map<UniqueId, Proposition> references, KnowledgeSourceCache ksCache, final Set<Proposition> cache) { //First, aggregate derived values according to the specified behavior. List<Proposition> derived = null; switch (this.behavior) { case SINGLE_FORWARD: derived = filterMatches(proposition, forwardDerivations.get(proposition), cache); break; case SINGLE_BACKWARD: derived = filterMatches(proposition, backwardDerivations.get(proposition), cache); break; case MULT_FORWARD: populateKnowledgeTree(ksCache); if (this.knowledgeTree.contains(proposition.getId())) { /* * For performance, pull the specified proposition ids, * cache the hierarchy, and check to see if each * traversed-to proposition's id is in the hierarchy. This * is much faster than traversing the whole hierarchy in * most cases. */ derived = new ArrayList<>(); internalDerived.add(proposition); while (!internalDerived.isEmpty()) { Proposition prop = internalDerived.remove(); /* * If (in hierarchy or ???) and not in cache, then add * to derived. */ Collection<Proposition> c = forwardDerivations.get(prop); if (c != null) { for (Proposition p : c) { if (cache.add(p)) { internalDerived.add(p); if (isMatch(p) && hasAllowedValue(p)) { derived.add(p); } } } } } } break; case MULT_BACKWARD: derived = new ArrayList<>(); internalDerived.add(proposition); while (!internalDerived.isEmpty()) { Proposition prop = internalDerived.remove(); Collection<Proposition> c = backwardDerivations.get(prop); if (c != null) { for (Proposition p : c) { if (cache.add(p)) { internalDerived.add(p); if (isMatch(p) && hasAllowedValue(p)) { derived.add(p); } } } } } break; default: throw new AssertionError("Unexpected behavior: " + this.behavior); } this.internalDerived.clear(); return createResults(derived); } @Override public String toString() { return ToStringBuilder.reflectionToString(this); } private void populateKnowledgeTree(KnowledgeSourceCache ksCache) { if (this.knowledgeTree == null) { this.knowledgeTree = new HashSet<>(); Queue<String> queue = new LinkedList<>(); Arrays.addAll(queue, getPropositionIds()); String propId; while ((propId = queue.poll()) != null) { PropositionDefinition propDef = ksCache.get(propId); if (propDef != null) { if (propDef.getInDataSource()) { this.knowledgeTree.add(propDef.getId()); } Arrays.addAll(queue, propDef.getChildren()); } else { throw new AssertionError("Invalid proposition definition " + propId); } } } } private boolean hasAllowedValue(Proposition proposition) { if (this.allowedValues.length == 0) { return true; } else { if (proposition instanceof Parameter && Arrays.contains(this.allowedValues, ((Parameter) proposition).getValue())) { return true; } else { return false; } } } /** * Filters <code>propositions</code>. Removes duplicates, propositions not * in the proposition id list, and parameters that do not meet any value * constraints set. * * @param propositions the {@link Collection<Proposition>} to filter. * @param cache a {@link Set<Proposition>} that is used to find duplicates. * * @return a newly-created {@link List<Proposition>}. */ private List<Proposition> filterMatches(Proposition inProposition, Collection<Proposition> propositions, Set<Proposition> cache) { TemporalProposition inTp = null; if (this.relation != null) { if (inProposition instanceof TemporalProposition) { inTp = (TemporalProposition) inProposition; } } List<Proposition> result = new ArrayList<>(); if (propositions != null) { for (Proposition proposition : propositions) { if (cache.add(proposition) && isMatch(proposition) && hasAllowedValue(proposition)) { if (inTp != null && proposition instanceof TemporalProposition) { TemporalProposition tp = (TemporalProposition) proposition; if (!this.relation.hasRelation(tp.getInterval(), inTp.getInterval())) { continue; } } result.add(proposition); } } } return result; } public Behavior getBehavior() { return behavior; } public Value[] getAllowedValues() { return allowedValues; } public Relation getRelation() { return relation; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + java.util.Arrays.hashCode(allowedValues); result = prime * result + ((behavior == null) ? 0 : behavior.hashCode()); result = prime * result + ((knowledgeTree == null) ? 0 : knowledgeTree.hashCode()); result = prime * result + ((relation == null) ? 0 : relation.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } Derivation other = (Derivation) obj; if (!java.util.Arrays.equals(allowedValues, other.allowedValues)) { return false; } if (behavior != other.behavior) { return false; } if (knowledgeTree == null) { if (other.knowledgeTree != null) { return false; } } else if (!knowledgeTree.equals(other.knowledgeTree)) { return false; } if (relation == null) { if (other.relation != null) { return false; } } else if (!relation.equals(other.relation)) { return false; } return true; } }