001/*
002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id: StatementImpl.java 20796 2007-06-19 09:52:03Z sfermigier $
020 */
021
022package org.nuxeo.ecm.platform.relations.api.impl;
023
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.HashMap;
027import java.util.List;
028import java.util.Map;
029
030import org.nuxeo.ecm.platform.relations.api.Blank;
031import org.nuxeo.ecm.platform.relations.api.Node;
032import org.nuxeo.ecm.platform.relations.api.Resource;
033import org.nuxeo.ecm.platform.relations.api.Statement;
034import org.nuxeo.ecm.platform.relations.api.Subject;
035import org.nuxeo.ecm.platform.relations.api.exceptions.InvalidPredicateException;
036import org.nuxeo.ecm.platform.relations.api.exceptions.InvalidStatementException;
037import org.nuxeo.ecm.platform.relations.api.exceptions.InvalidSubjectException;
038
039/**
040 * Statement with subject, predicate and object.
041 *
042 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
043 */
044// TODO: make a statement handle metadata (as properties)
045public class StatementImpl implements Statement {
046
047    private static final long serialVersionUID = 1L;
048
049    protected Subject subject;
050
051    protected Resource predicate;
052
053    protected Node object;
054
055    protected Map<Resource, Node[]> properties = new HashMap<Resource, Node[]>();
056
057    /**
058     * Constructor for NULL statement.
059     */
060    public StatementImpl() {
061    }
062
063    /**
064     * Constructor.
065     *
066     * @param subject Resource or Blank node
067     * @param predicate Resource
068     * @param object Resource, Blank or Literal node
069     */
070    public StatementImpl(Node subject, Node predicate, Node object) {
071        boolean validSubject = true;
072        try {
073            setSubject(subject);
074        } catch (InvalidSubjectException e) {
075            validSubject = false;
076        }
077        boolean validPredicate = true;
078        try {
079            setPredicate(predicate);
080        } catch (InvalidPredicateException e) {
081            validPredicate = false;
082        }
083        if (!validPredicate && !validSubject) {
084            throw new InvalidStatementException();
085        } else if (!validSubject) {
086            throw new InvalidSubjectException();
087        } else if (!validPredicate) {
088            throw new InvalidPredicateException();
089        }
090        this.object = object;
091    }
092
093    public Node getObject() {
094        return object;
095    }
096
097    public void setObject(Node object) {
098        this.object = object;
099    }
100
101    public Resource getPredicate() {
102        return predicate;
103    }
104
105    public void setPredicate(Node predicate) {
106        if (predicate != null && !predicate.isResource()) {
107            throw new InvalidPredicateException();
108        }
109        this.predicate = (Resource) predicate;
110    }
111
112    public Subject getSubject() {
113        return subject;
114    }
115
116    public void setSubject(Node subject) {
117        if (subject != null) {
118            if (subject instanceof Subject) {
119                this.subject = (Subject) subject;
120            } else {
121                throw new InvalidSubjectException();
122            }
123        }
124    }
125
126    public Map<Resource, Node[]> getProperties() {
127        return properties;
128    }
129
130    public Map<String, Node[]> getStringProperties() {
131        Map<String, Node[]> stringProps = new HashMap<String, Node[]>();
132        for (Map.Entry<Resource, Node[]> property : properties.entrySet()) {
133            stringProps.put(property.getKey().getUri(), property.getValue());
134        }
135        return stringProps;
136    }
137
138    public Node getProperty(Resource property) {
139        // return first one found
140        Node node = null;
141        Node[] values = properties.get(property);
142        if (values != null && values.length > 0) {
143            node = values[0];
144        }
145        return node;
146    }
147
148    public Node[] getProperties(Resource property) {
149        Node[] values = properties.get(property);
150        return values;
151    }
152
153    public void setProperties(Map<Resource, Node[]> properties) {
154        if (properties != null) {
155            for (Map.Entry<Resource, Node[]> property : properties.entrySet()) {
156                setProperties(property.getKey(), property.getValue());
157            }
158        } else {
159            this.properties.clear();
160        }
161    }
162
163    public void setProperty(Resource property, Node value) {
164        if (property != null && value != null) {
165            Node[] values = { value };
166            properties.put(property, values);
167        }
168    }
169
170    public void setProperties(Resource property, Node[] values) {
171        if (property != null && values != null && values.length > 0) {
172            properties.put(property, values);
173        }
174    }
175
176    public void deleteProperties() {
177        properties.clear();
178    }
179
180    public void deleteProperty(Resource property) {
181        properties.remove(property);
182    }
183
184    public void deleteProperty(Resource property, Node value) {
185        if (properties.containsKey(property)) {
186            List<Node> valuesList = new ArrayList<Node>();
187            valuesList.addAll(Arrays.asList(properties.get(property)));
188            valuesList.remove(value);
189            if (valuesList.isEmpty()) {
190                properties.remove(property);
191            } else {
192                properties.put(property, valuesList.toArray(new Node[] {}));
193            }
194        }
195    }
196
197    public void deleteProperties(Resource property, Node[] values) {
198        if (properties.containsKey(property) && values != null && values.length > 0) {
199            List<Node> valuesList = new ArrayList<Node>();
200            valuesList.addAll(Arrays.asList(properties.get(property)));
201            valuesList.removeAll(Arrays.asList(values));
202            if (valuesList.isEmpty()) {
203                properties.remove(property);
204            } else {
205                properties.put(property, valuesList.toArray(new Node[] {}));
206            }
207        }
208    }
209
210    public void addProperties(Map<Resource, Node[]> properties) {
211        if (properties != null) {
212            for (Map.Entry<Resource, Node[]> property : properties.entrySet()) {
213                addProperties(property.getKey(), property.getValue());
214            }
215        }
216    }
217
218    public void addProperty(Resource property, Node value) {
219        if (property != null && value != null) {
220            if (properties.containsKey(property)) {
221                List<Node> valuesList = new ArrayList<Node>();
222                valuesList.addAll(Arrays.asList(properties.get(property)));
223                if (!valuesList.contains(value)) {
224                    valuesList.add(value);
225                    properties.put(property, valuesList.toArray(new Node[] {}));
226                }
227            } else {
228                Node[] values = { value };
229                properties.put(property, values);
230            }
231        }
232    }
233
234    public void addProperties(Resource property, Node[] values) {
235        if (property != null && values != null && values.length > 0) {
236            if (properties.containsKey(property)) {
237                // add only missing nodes
238                List<Node> valuesList = new ArrayList<Node>();
239                valuesList.addAll(Arrays.asList(properties.get(property)));
240                boolean changed = false;
241                for (Node value : values) {
242                    if (!valuesList.contains(value)) {
243                        valuesList.add(value);
244                        changed = true;
245                    }
246                }
247                if (changed) {
248                    properties.put(property, valuesList.toArray(new Node[] {}));
249                }
250            } else {
251                properties.put(property, values);
252            }
253        }
254    }
255
256    @Override
257    public String toString() {
258        return String.format("%s(%s, %s, %s)", getClass().getSimpleName(), subject, predicate, object);
259    }
260
261    @Override
262    public boolean equals(Object other) {
263        if (this == other) {
264            return true;
265        }
266        if (!(other instanceof StatementImpl)) {
267            return false;
268        }
269        StatementImpl otherStatement = (StatementImpl) other;
270        return subject.equals(otherStatement.subject) && predicate.equals(otherStatement.predicate)
271                && object.equals(otherStatement.object);
272    }
273
274    @Override
275    public int hashCode() {
276        int result = 17;
277        result = 17 * result + subject.hashCode();
278        result = 17 * result + predicate.hashCode();
279        result = 17 * result + object.hashCode();
280        return result;
281    }
282
283    public int compareTo(Statement o) {
284        // dumb implementation, just used to compare statements lists
285        return toString().compareTo(o.toString());
286    }
287
288    @Override
289    public Object clone() {
290        StatementImpl clone;
291        try {
292            clone = (StatementImpl) super.clone();
293        } catch (CloneNotSupportedException e) {
294            throw new RuntimeException(e);
295        }
296        Map<Resource, Node[]> clonedProperties = new HashMap<Resource, Node[]>();
297        for (Map.Entry<Resource, Node[]> property : properties.entrySet()) {
298            clonedProperties.put(property.getKey(), property.getValue().clone());
299        }
300        clone.properties = clonedProperties;
301        return clone;
302    }
303
304}