Skip to content

Method: static {...}

1: /**
2: * Copyright (C) 2016 Czech Technical University in Prague
3: * <p/>
4: * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
5: * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
6: * version.
7: * <p/>
8: * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10: * details. You should have received a copy of the GNU General Public License along with this program. If not, see
11: * <http://www.gnu.org/licenses/>.
12: */
13: package cz.cvut.kbss.jopa.oom;
14:
15: import cz.cvut.kbss.jopa.CommonVocabulary;
16: import cz.cvut.kbss.jopa.exceptions.InvalidAssertionIdentifierException;
17: import cz.cvut.kbss.jopa.model.IRI;
18: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
19: import cz.cvut.kbss.jopa.model.metamodel.Attribute;
20: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
21: import cz.cvut.kbss.jopa.model.metamodel.PropertiesSpecification;
22: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
23: import cz.cvut.kbss.ontodriver.model.Assertion;
24: import cz.cvut.kbss.ontodriver.model.Axiom;
25: import cz.cvut.kbss.ontodriver.model.Value;
26:
27: import java.util.*;
28: import java.util.Map.Entry;
29: import java.util.stream.Collectors;
30:
31: public class PropertiesFieldStrategy<X> extends
32: FieldStrategy<PropertiesSpecification<? super X, ?>, X> {
33:
34: private final Map<String, Set<String>> values;
35:
36: PropertiesFieldStrategy(EntityType<X> et, PropertiesSpecification<? super X, ?> att,
37: Descriptor descriptor, EntityMappingHelper mapper) {
38: super(et, att, descriptor, mapper);
39: this.values = new HashMap<>();
40: }
41:
42: @Override
43: void addValueFromAxiom(Axiom<?> ax) {
44: final String property = ax.getAssertion().getIdentifier().toString();
45: if (shouldSkipAxiom(ax)) {
46: return;
47: }
48: if (!values.containsKey(property)) {
49: values.put(property, new HashSet<>());
50: }
51: final String value = ax.getValue().stringValue();
52: values.get(property).add(value);
53: }
54:
55: private boolean shouldSkipAxiom(Axiom<?> ax) {
56: final String property = ax.getAssertion().getIdentifier().toString();
57: // This is class assertion for entities without types
58: return property.equals(CommonVocabulary.RDF_TYPE) || isMappedAttribute(ax);
59: }
60:
61: private boolean isMappedAttribute(Axiom<?> ax) {
62: // TODO This is too simple, in case the assertion corresponds to a mapped attribute,
63: // we also have to check whether the value is suitable for the attribute. If not, it belongs to properties
64: final IRI propertyAsIri = IRI.create(ax.getAssertion().getIdentifier().toString());
65: for (Attribute<?, ?> att : et.getAttributes()) {
66: if (att.getIRI().equals(propertyAsIri)) {
67: return true;
68: }
69: }
70: return false;
71: }
72:
73: @Override
74: void buildInstanceFieldValue(Object instance) throws IllegalAccessException {
75: if (values.isEmpty()) {
76: return;
77: }
78: setValueOnInstance(instance, values);
79: }
80:
81: @Override
82: void buildAxiomValuesFromInstance(X instance, AxiomValueGatherer valueBuilder) throws IllegalAccessException {
83: final Object val = extractFieldValueFromInstance(instance);
84: final X original = mapper.getOriginalInstance(instance);
85: if (val == null) {
86: if (original == null) {
87: return;
88: }
89: final Map<?, Set<?>> origProps = (Map<?, Set<?>>) extractFieldValueFromInstance(
90: original);
91: if (origProps == null || origProps.isEmpty()) {
92: return;
93: }
94: valueBuilder.removeProperties(prepareProperties(origProps), getAttributeContext());
95: return;
96: }
97: assert val instanceof Map;
98: final Map<?, Set<?>> props = (Map<?, Set<?>>) val;
99: if (original == null) {
100: valueBuilder.addProperties(prepareProperties(props), getAttributeContext());
101: } else {
102: final Map<?, Set<?>> origProps = (Map<?, Set<?>>) extractFieldValueFromInstance(
103: original);
104: final Map<Assertion, Set<Value<?>>> toRemove = resolvePropertiesToRemove(props, origProps);
105: if (!toRemove.isEmpty()) {
106: valueBuilder.removeProperties(toRemove, getAttributeContext());
107: }
108: final Map<Assertion, Set<Value<?>>> toAdd = resolvePropertiesToAdd(props, origProps);
109: if (!toAdd.isEmpty()) {
110: valueBuilder.addProperties(toAdd, getAttributeContext());
111: }
112: }
113: }
114:
115: private Map<Assertion, Set<Value<?>>> prepareProperties(Map<?, Set<?>> props) {
116: final Map<Assertion, Set<Value<?>>> result = new HashMap<>(props.size());
117: for (Entry<?, Set<?>> entry : props.entrySet()) {
118: result.put(propertyToAssertion(entry.getKey()), objectsToValues(entry.getValue()));
119: }
120: return result;
121: }
122:
123: private Assertion propertyToAssertion(Object property) {
124: try {
125: return Assertion
126: .createPropertyAssertion(EntityPropertiesUtils.getValueAsURI(property), attribute.isInferred());
127: } catch (IllegalArgumentException e) {
128: throw new InvalidAssertionIdentifierException(property + " is not a valid identifier.", e);
129: }
130: }
131:
132: private Set<Value<?>> objectsToValues(Collection<?> strValues) {
133: final Set<Value<?>> ontoValues = new HashSet<>(strValues.size());
134: ontoValues.addAll(strValues.stream().map(Value::new).collect(Collectors.toList()));
135: return ontoValues;
136: }
137:
138: private Map<Assertion, Set<Value<?>>> resolvePropertiesToRemove(Map<?, Set<?>> current,
139: Map<?, Set<?>> original) {
140: return propertyDiff(original, current);
141: }
142:
143: /**
144: * The difference is counted as the properties and values which were in base but are not in the updated version.
145: */
146: private Map<Assertion, Set<Value<?>>> propertyDiff(Map<?, Set<?>> base,
147: Map<?, Set<?>> updated) {
148: if (base == null || base.isEmpty()) {
149: return Collections.emptyMap();
150: }
151: final Map<Assertion, Set<Value<?>>> diff = new HashMap<>();
152: if (updated == null || updated.isEmpty()) {
153: diff.putAll(createAssertionsForAll(base));
154: } else {
155: for (Entry<?, Set<?>> entry : base.entrySet()) {
156: final Object key = entry.getKey();
157: if (!updated.containsKey(key) || updated.get(key) == null || updated.get(key).isEmpty()) {
158: // All values of the property are missing
159: diff.put(propertyToAssertion(key), objectsToValues(entry.getValue()));
160: } else {
161: final Set<?> currentValues = updated.get(key);
162: // Check which property values are missing
163: final List<?> removed =
164: entry.getValue().stream().filter(origVal -> !currentValues.contains(origVal))
165: .collect(Collectors.toList());
166: if (!removed.isEmpty()) {
167: diff.put(propertyToAssertion(key), objectsToValues(removed));
168: }
169: }
170: }
171: }
172: return diff;
173: }
174:
175: private Map<Assertion, Set<Value<?>>> createAssertionsForAll(Map<?, Set<?>> map) {
176: final Map<Assertion, Set<Value<?>>> diff = new HashMap<>(map.size());
177: map.forEach((key, value) -> diff.put(propertyToAssertion(key), objectsToValues(value)));
178: return diff;
179: }
180:
181: private Map<Assertion, Set<Value<?>>> resolvePropertiesToAdd(Map<?, Set<?>> current,
182: Map<?, Set<?>> original) {
183: return propertyDiff(current, original);
184: }
185:
186: @Override
187: Assertion createAssertion() {
188: return Assertion.createUnspecifiedPropertyAssertion(attribute.isInferred());
189: }
190:
191: }