/* * Copyright (c) 2016 Evolveum * * 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. */ package com.evolveum.midpoint.model.impl.lens; import java.util.*; import java.util.Map.Entry; import java.util.stream.Collectors; import javax.xml.namespace.QName; import com.evolveum.midpoint.model.api.context.EvaluationOrder; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.util.DebugUtil; import com.evolveum.midpoint.util.QNameUtil; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections4.MultiSet; import org.jetbrains.annotations.NotNull; /** * @author semancik * */ public class EvaluationOrderImpl implements EvaluationOrder { public static EvaluationOrder UNDEFINED = new UndefinedEvaluationOrderImpl(); public static EvaluationOrder ZERO = createZero(); public static EvaluationOrder ONE = ZERO.advance(SchemaConstants.ORG_DEFAULT); @NotNull private final HashMap<QName, Integer> orderMap; // see checkConsistence private void checkConsistence() { if (CHECK_CONSISTENCE) { orderMap.forEach((r, v) -> { if (r == null || QNameUtil.noNamespace(r)) { throw new IllegalStateException("Null or unqualified relation " + r + " in " + this); } if (v == null) { throw new IllegalStateException("Null value in for relation " + r + " in " + this); } }); } } private static final boolean CHECK_CONSISTENCE = true; private EvaluationOrderImpl() { orderMap = new HashMap<>(); } private EvaluationOrderImpl(EvaluationOrderImpl that) { this.orderMap = new HashMap<>(that.orderMap); } private static EvaluationOrderImpl createZero() { EvaluationOrderImpl eo = new EvaluationOrderImpl(); eo.orderMap.put(SchemaConstants.ORG_DEFAULT, 0); return eo; } @Override public int getSummaryOrder() { int rv = 0; for (Entry<QName, Integer> entry : orderMap.entrySet()) { if (!ObjectTypeUtil.isDelegationRelation(entry.getKey())) { rv += entry.getValue(); } } return rv; } @Override public EvaluationOrder advance(QName relation) { checkConsistence(); return advance(relation, 1); } private EvaluationOrder advance(QName relation, int amount) { EvaluationOrderImpl clone = clone(); clone.advanceThis(relation, amount); clone.checkConsistence(); return clone; } @Override public EvaluationOrder decrease(MultiSet<QName> relations) { EvaluationOrderImpl clone = clone(); for (QName relation : relations) { clone.advanceThis(relation, -1); } clone.checkConsistence(); return clone; } // must always be private: public interface will not allow to modify object state! private void advanceThis(QName relation, int amount) { relation = ObjectTypeUtil.normalizeRelation(relation); orderMap.put(relation, getMatchingRelationOrder(relation) + amount); } @Override public int getMatchingRelationOrder(QName relation) { checkConsistence(); return orderMap.getOrDefault(ObjectTypeUtil.normalizeRelation(relation), 0); } @Override public EvaluationOrder resetOrder(QName relation, int newOrder) { EvaluationOrderImpl clone = clone(); clone.orderMap.put(ObjectTypeUtil.normalizeRelation(relation), newOrder); clone.checkConsistence(); return clone; } @Override public Map<QName, Integer> diff(EvaluationOrder newState) { if (!newState.isDefined()) { throw new IllegalArgumentException("Cannot compute diff to undefined evaluation order"); } @SuppressWarnings({"unchecked", "raw"}) Collection<QName> relations = CollectionUtils.union(getRelations(), newState.getRelations()); Map<QName, Integer> rv = new HashMap<>(); relations.forEach(relation -> rv.put(relation, newState.getMatchingRelationOrder(relation) - getMatchingRelationOrder(relation))); return rv; } @Override public EvaluationOrder applyDifference(Map<QName,Integer> difference) { EvaluationOrderImpl clone = clone(); difference.forEach((relation, count) -> clone.advanceThis(relation, count)); clone.checkConsistence(); return clone; } @Override public boolean isDefined() { return true; } @Override public Set<QName> getRelations() { return orderMap.keySet(); } @Override public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); DebugUtil.debugDumpLabelLn(sb, "EvaluationOrder", indent); DebugUtil.debugDumpWithLabelLn(sb, "summaryOrder", getSummaryOrder(), indent + 1); DebugUtil.debugDumpWithLabel(sb, "orderMap", orderMap, indent + 1); return sb.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof EvaluationOrderImpl)) return false; EvaluationOrderImpl that = (EvaluationOrderImpl) o; return Objects.equals(orderMap, that.orderMap); } @Override public int hashCode() { return Objects.hash(orderMap); } @Override public String toString() { return "EvaluationOrder(" + shortDump() + ")"; } @Override public String shortDump() { StringBuilder sb = new StringBuilder(); for (Entry<QName,Integer> entry: orderMap.entrySet()) { if (entry.getKey() != null) { sb.append(entry.getKey().getLocalPart()); } else { sb.append("null"); // actually shouldn't occur (relations are normalized) } sb.append(":"); sb.append(entry.getValue()); sb.append(","); } sb.setLength(sb.length() - 1); sb.append("=").append(getSummaryOrder()); return sb.toString(); } @Override public Collection<QName> getExtraRelations() { return orderMap.entrySet().stream() .filter(e -> !ObjectTypeUtil.isMembershipRelation(e.getKey()) && !ObjectTypeUtil.isDelegationRelation(e.getKey()) && e.getValue() > 0) .map(e -> e.getKey()) .collect(Collectors.toSet()); } @Override public EvaluationOrderImpl clone() { return new EvaluationOrderImpl(this); } @Override public boolean isValid() { return orderMap.values().stream().allMatch(c -> c >= 0); } }