/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* bstefanescu
*
* $Id$
*/
package org.eclipse.ecr.core.api.model.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.ecr.core.api.model.DocumentPart;
import org.eclipse.ecr.core.api.model.Property;
import org.eclipse.ecr.core.api.model.PropertyDiff;
import org.eclipse.ecr.core.api.model.PropertyException;
import org.eclipse.ecr.core.api.model.PropertyFactory;
import org.eclipse.ecr.core.api.model.PropertyRuntimeException;
import org.eclipse.ecr.core.api.model.PropertyVisitor;
import org.eclipse.ecr.core.api.model.ValueExporter;
import org.eclipse.ecr.core.schema.SchemaManager;
import org.eclipse.ecr.core.schema.types.ComplexType;
import org.eclipse.ecr.core.schema.types.Field;
import org.eclipse.ecr.core.schema.types.Schema;
import org.eclipse.ecr.runtime.api.Framework;
import org.nuxeo.common.utils.Path;
/**
*
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*
*/
public class DocumentPartImpl extends ComplexProperty implements DocumentPart {
private static final long serialVersionUID = -2959928612693829263L;
protected transient Schema schema;
protected transient PropertyFactory factory;
public DocumentPartImpl(Schema schema, PropertyFactory factory) {
super(null);
this.schema = schema;
this.factory = factory == null ? DefaultPropertyFactory.getInstance() : factory;
}
public DocumentPartImpl(Schema schema) {
this(schema, null);
}
@Override
public void internalSetValue(Serializable value) throws PropertyException {
}
@Override
public boolean isContainer() {
return true;
}
@Override
public Schema getSchema() {
return schema;
}
@Override
public String getName() {
return schema.getName();
}
@Override
public Schema getType() {
return schema;
}
@Override
public Field getField() {
throw new UnsupportedOperationException(
"Document parts are not bound to schema fields");
}
@Override
public Path collectPath(Path path) {
return path;
}
@Override
public Object clone() throws CloneNotSupportedException {
try {
Map<String, Serializable> value = exportValues();
DocumentPartImpl dp = new DocumentPartImpl(schema);
dp.importValues(value); //TODO: should preserve property flags?
return dp;
} catch (PropertyException e) {
throw new PropertyRuntimeException("clone failed", e);
}
}
@Override
public Map<String, Serializable> exportValues() throws PropertyException {
ValueExporter exporter = new ValueExporter();
return exporter.run(this);
}
@Override
public void importValues(Map<String, Serializable> values) throws PropertyException {
init((Serializable) values);
}
@Override
public void accept(PropertyVisitor visitor, Object arg) throws PropertyException {
arg = visitor.visit(this, arg);
if (arg != null) {
visitChildren(visitor, arg);
}
}
@Override
public Property createProperty(Property parent, Field field) {
return createProperty(parent, field, 0);
}
@Override
public Property createProperty(Property parent, Field field, int flags) {
return factory.createProperty(parent, field, flags);
}
@Override
public PropertyDiff exportDiff() {
return null;
}
@Override
public void importDiff(PropertyDiff diff) {
}
private void readObject(ObjectInputStream in)
throws ClassNotFoundException, IOException {
// always perform the default de-serialization first
in.defaultReadObject();
try {
deserialize(in);
} catch (PropertyException e) {
IOException ee = new IOException(
"failed to deserialize document part " + schema);
ee.initCause(e);
throw ee;
}
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
try {
serialize(out);
} catch (PropertyException e) {
IOException ee = new IOException(
"failed to serialize document part " + schema);
ee.initCause(e);
throw ee;
}
}
public void serialize(ObjectOutputStream out) throws PropertyException, IOException {
// write schema
out.writeObject(schema.getName());
// write factory
if (factory == null || factory == DefaultPropertyFactory.getInstance()) {
out.writeObject(null);
} else if (factory != null) {
out.writeObject(factory);
}
// write children
Collection<Property> props = getNonPhantomChildren();
int size = props.size();
out.writeInt(size);
if (size > 0) {
for (Property child : props) {
serializeProperty(child, out);
}
}
}
private static void serializeProperty(Property prop, ObjectOutputStream out)
throws PropertyException, IOException {
AbstractProperty ap = (AbstractProperty) prop;
out.writeObject(prop.getName());
out.writeObject(ap.data);
out.writeInt(ap.flags);
if (!prop.isContainer()) {
out.writeObject(prop.getValue());
return;
}
Collection<Property> props = null;
if (prop.isList()) {
props = prop.getChildren();
} else {
props = ((ComplexProperty) prop).getNonPhantomChildren();
}
int size = props.size();
out.writeInt(size);
if (size > 0) {
for (Property child : props) {
serializeProperty(child, out);
}
}
}
public void deserialize(ObjectInputStream in)
throws ClassNotFoundException, IOException, PropertyException {
// read schema
String schemaName = (String) in.readObject();
//schema = TypeService.getSchemaManager().getSchema(schemaName);
schema = Framework.getLocalService(SchemaManager.class).getSchema(schemaName);
// read factory
factory = (PropertyFactory) in.readObject();
if (factory == null) {
factory = DefaultPropertyFactory.getInstance();
}
// read children
deserializeChildren(this, in);
}
public void deserializeChildren(ListProperty parent, ObjectInputStream in)
throws ClassNotFoundException, IOException, PropertyException {
int size = in.readInt();
if (size < 1) {
return;
}
Field field = parent.getType().getField();
for (int i=0; i<size; i++) {
// read name
in.readObject(); // name is not used for list children
Object data = in.readObject();
// read flags
int flags = in.readInt();
Property prop = createProperty(parent, field, flags);
((AbstractProperty) prop).data = data;
if (!prop.isContainer()) {
prop.init((Serializable) in.readObject());
} else if (prop.isList()) {
deserializeChildren((ListProperty) prop, in);
} else {
deserializeChildren((ComplexProperty) prop, in);
}
// add property to parent
parent.children.add(prop);
}
}
public void deserializeChildren(ComplexProperty parent, ObjectInputStream in)
throws ClassNotFoundException, IOException, PropertyException {
// children are transient so we need to create them explicitely
parent.children = new Hashtable<String, Property>();
// read serialized children
int size = in.readInt();
if (size < 1) {
return;
}
ComplexType type = parent.getType();
for (int i=0; i<size; i++) {
// read name
String name = (String) in.readObject();
Object data = in.readObject();
// read flags
int flags = in.readInt();
Property prop = createProperty(parent, type.getField(name), flags);
((AbstractProperty) prop).data = data;
if (!prop.isContainer()) {
prop.init((Serializable) in.readObject());
} else if (prop.isList()) {
deserializeChildren((ListProperty) prop, in);
} else {
deserializeChildren((ComplexProperty) prop, in);
}
// add property to parent
parent.children.put(prop.getName(), prop);
}
}
public boolean isSameAs(DocumentPart dp) {
if (dp == null) {
return false;
}
if (dp instanceof ComplexProperty) {
return getNonPhantomChildren().equals(
((ComplexProperty) dp).getNonPhantomChildren());
}
return false;
}
@Override
public String toString() {
return getClass().getSimpleName() + '(' + getName()
+ (isDirty() ? "*" : "") + ", " + children + ')';
}
}