/*
* (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* Nuxeo - initial API and implementation
*
* $Id: StatementImpl.java 20796 2007-06-19 09:52:03Z sfermigier $
*/
package org.nuxeo.ecm.platform.relations.api.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.nuxeo.ecm.platform.relations.api.Blank;
import org.nuxeo.ecm.platform.relations.api.Node;
import org.nuxeo.ecm.platform.relations.api.Resource;
import org.nuxeo.ecm.platform.relations.api.Statement;
import org.nuxeo.ecm.platform.relations.api.Subject;
import org.nuxeo.ecm.platform.relations.api.exceptions.InvalidPredicateException;
import org.nuxeo.ecm.platform.relations.api.exceptions.InvalidStatementException;
import org.nuxeo.ecm.platform.relations.api.exceptions.InvalidSubjectException;
/**
* Statement with subject, predicate and object.
*
* @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
*/
// TODO: make a statement handle metadata (as properties)
public class StatementImpl implements Statement {
private static final long serialVersionUID = 1L;
protected Subject subject;
protected Resource predicate;
protected Node object;
protected Map<Resource, Node[]> properties = new HashMap<Resource, Node[]>();
/**
* Constructor for NULL statement.
*/
public StatementImpl() {
}
/**
* Constructor.
*
* @param subject Resource or Blank node
* @param predicate Resource
* @param object Resource, Blank or Literal node
*/
public StatementImpl(Node subject, Node predicate, Node object) {
boolean validSubject = true;
try {
setSubject(subject);
} catch (InvalidSubjectException e) {
validSubject = false;
}
boolean validPredicate = true;
try {
setPredicate(predicate);
} catch (InvalidPredicateException e) {
validPredicate = false;
}
if (!validPredicate && !validSubject) {
throw new InvalidStatementException();
} else if (!validSubject) {
throw new InvalidSubjectException();
} else if (!validPredicate) {
throw new InvalidPredicateException();
}
this.object = object;
}
public Node getObject() {
return object;
}
public void setObject(Node object) {
this.object = object;
}
public Resource getPredicate() {
return predicate;
}
public void setPredicate(Node predicate) {
if (predicate != null && !predicate.isResource()) {
throw new InvalidPredicateException();
}
this.predicate = (Resource) predicate;
}
public Subject getSubject() {
return subject;
}
public void setSubject(Node subject) {
if (subject != null) {
if (subject instanceof Subject) {
this.subject = (Subject) subject;
} else {
throw new InvalidSubjectException();
}
}
}
public Map<Resource, Node[]> getProperties() {
return properties;
}
public Map<String, Node[]> getStringProperties() {
Map<String, Node[]> stringProps = new HashMap<String, Node[]>();
for (Map.Entry<Resource, Node[]> property : properties.entrySet()) {
stringProps.put(property.getKey().getUri(), property.getValue());
}
return stringProps;
}
public Node getProperty(Resource property) {
// return first one found
Node node = null;
Node[] values = properties.get(property);
if (values != null && values.length > 0) {
node = values[0];
}
return node;
}
public Node[] getProperties(Resource property) {
Node[] values = properties.get(property);
return values;
}
public void setProperties(Map<Resource, Node[]> properties) {
if (properties != null) {
for (Map.Entry<Resource, Node[]> property : properties.entrySet()) {
setProperties(property.getKey(), property.getValue());
}
} else {
this.properties.clear();
}
}
public void setProperty(Resource property, Node value) {
if (property != null && value != null) {
Node[] values = { value };
properties.put(property, values);
}
}
public void setProperties(Resource property, Node[] values) {
if (property != null && values != null && values.length > 0) {
properties.put(property, values);
}
}
public void deleteProperties() {
properties.clear();
}
public void deleteProperty(Resource property) {
properties.remove(property);
}
public void deleteProperty(Resource property, Node value) {
if (properties.containsKey(property)) {
List<Node> valuesList = new ArrayList<Node>();
valuesList.addAll(Arrays.asList(properties.get(property)));
valuesList.remove(value);
if (valuesList.isEmpty()) {
properties.remove(property);
} else {
properties.put(property, valuesList.toArray(new Node[] {}));
}
}
}
public void deleteProperties(Resource property, Node[] values) {
if (properties.containsKey(property) && values != null && values.length > 0) {
List<Node> valuesList = new ArrayList<Node>();
valuesList.addAll(Arrays.asList(properties.get(property)));
valuesList.removeAll(Arrays.asList(values));
if (valuesList.isEmpty()) {
properties.remove(property);
} else {
properties.put(property, valuesList.toArray(new Node[] {}));
}
}
}
public void addProperties(Map<Resource, Node[]> properties) {
if (properties != null) {
for (Map.Entry<Resource, Node[]> property : properties.entrySet()) {
addProperties(property.getKey(), property.getValue());
}
}
}
public void addProperty(Resource property, Node value) {
if (property != null && value != null) {
if (properties.containsKey(property)) {
List<Node> valuesList = new ArrayList<Node>();
valuesList.addAll(Arrays.asList(properties.get(property)));
if (!valuesList.contains(value)) {
valuesList.add(value);
properties.put(property, valuesList.toArray(new Node[] {}));
}
} else {
Node[] values = { value };
properties.put(property, values);
}
}
}
public void addProperties(Resource property, Node[] values) {
if (property != null && values != null && values.length > 0) {
if (properties.containsKey(property)) {
// add only missing nodes
List<Node> valuesList = new ArrayList<Node>();
valuesList.addAll(Arrays.asList(properties.get(property)));
boolean changed = false;
for (Node value : values) {
if (!valuesList.contains(value)) {
valuesList.add(value);
changed = true;
}
}
if (changed) {
properties.put(property, valuesList.toArray(new Node[] {}));
}
} else {
properties.put(property, values);
}
}
}
@Override
public String toString() {
return String.format("%s(%s, %s, %s)", getClass().getSimpleName(), subject, predicate, object);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof StatementImpl)) {
return false;
}
StatementImpl otherStatement = (StatementImpl) other;
return subject.equals(otherStatement.subject) && predicate.equals(otherStatement.predicate)
&& object.equals(otherStatement.object);
}
@Override
public int hashCode() {
int result = 17;
result = 17 * result + subject.hashCode();
result = 17 * result + predicate.hashCode();
result = 17 * result + object.hashCode();
return result;
}
public int compareTo(Statement o) {
// dumb implementation, just used to compare statements lists
return toString().compareTo(o.toString());
}
@Override
public Object clone() {
StatementImpl clone;
try {
clone = (StatementImpl) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
Map<Resource, Node[]> clonedProperties = new HashMap<Resource, Node[]>();
for (Map.Entry<Resource, Node[]> property : properties.entrySet()) {
clonedProperties.put(property.getKey(), property.getValue().clone());
}
clone.properties = clonedProperties;
return clone;
}
}