/*
* 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:
* Nuxeo - initial API and implementation
*
* $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
*/
package org.eclipse.ecr.core.api;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.ecr.core.api.impl.blob.StringBlob;
import org.eclipse.ecr.core.api.model.Property;
import org.eclipse.ecr.core.api.model.ReadOnlyPropertyException;
import org.eclipse.ecr.core.api.model.ValueExporter;
import org.eclipse.ecr.core.api.model.impl.DocumentPartImpl;
import org.eclipse.ecr.core.api.model.impl.ListProperty;
import org.eclipse.ecr.core.api.model.impl.MapProperty;
import org.eclipse.ecr.core.api.model.impl.primitives.DateProperty;
import org.eclipse.ecr.core.api.model.impl.primitives.LongProperty;
import org.eclipse.ecr.core.api.model.impl.primitives.StringProperty;
import org.eclipse.ecr.core.schema.SchemaManager;
import org.eclipse.ecr.core.schema.types.Schema;
import org.eclipse.ecr.runtime.RuntimeService;
import org.eclipse.ecr.runtime.api.Framework;
import org.eclipse.ecr.testlib.NXRuntimeTestCase;
/**
*
* @author Bogdan Stefanescu
*
*/
// We're declaring variables as HashMaps / ArrayLists so they can be
// Serializable
@SuppressWarnings({ "CollectionDeclaredAsConcreteClass" })
public class TestPropertyModel extends NXRuntimeTestCase {
protected RuntimeService runtime;
protected Schema schema;
protected DocumentPartImpl dp;
static <T> ArrayList<T> arrayList(T... args) {
ArrayList<T> list = new ArrayList<T>(args.length);
list.addAll(Arrays.asList(args));
return list;
}
private static class Name {
String firstName;
String lastName;
HashMap<String, Serializable> getMap() {
HashMap<String, Serializable> map = new HashMap<String, Serializable>();
map.put("lastName", lastName);
map.put("firstName", firstName);
return map;
}
}
private static class Author {
Name name = new Name();
Long age;
Author() {
}
Author(long age) {
this.age = age;
}
HashMap<String, Serializable> getMap() {
HashMap<String, Serializable> map = new HashMap<String, Serializable>();
map.put("name", name.getMap());
map.put("age", age);
return map;
}
}
private class FileName implements Serializable {
private static final long serialVersionUID = -3238719896844696496L;
String name;
String extension;
HashMap<String, Serializable> getMap() {
HashMap<String, Serializable> map = new HashMap<String, Serializable>();
map.put("name", name);
map.put("extension", extension);
return map;
}
}
private class BlobFile implements Serializable {
private static final long serialVersionUID = 4486693420148155780L;
final FileName fileName = new FileName();
StringBlob blob;
HashMap<String, Serializable> getMap() {
HashMap<String, Serializable> map = new HashMap<String, Serializable>();
map.put("fileName", fileName.getMap());
map.put("blob", blob);
return map;
}
}
private class Book {
private String title;
private Calendar creationDate;
private Long price;
private String[] keywords;
private ArrayList<String> references;
private ArrayList<Author> authors;
private BlobFile file;
HashMap<String, Serializable> getMap() {
HashMap<String, Serializable> map = new HashMap<String, Serializable>();
map.put("book:title", title);
map.put("book:creationDate", creationDate);
map.put("book:price", price);
map.put("book:keywords", keywords);
if (references == null) {
map.put("book:references", new ArrayList<Serializable>());
} else {
map.put("book:references", references);
}
map.put("book:file", file != null ? file.getMap()
: new HashMap<String, Serializable>());
if (authors == null) {
map.put("book:authors",
new ArrayList<HashMap<String, Serializable>>());
} else {
ArrayList<HashMap<String, Serializable>> list = new ArrayList<HashMap<String, Serializable>>();
for (Author author : authors) {
list.add(author.getMap());
}
map.put("book:authors", list);
}
return map;
}
}
@Override
public void setUp() throws Exception {
super.setUp();
deployBundle("org.eclipse.ecr.core.schema");
deployContrib("org.eclipse.ecr.core.api.test",
"OSGI-INF/test-propmodel-types-contrib.xml");
SchemaManager mgr = Framework.getService(SchemaManager.class);
// XSDLoader loader = new XSDLoader((SchemaManagerImpl) mgr);
// schema = loader.loadSchema("test", "book",
// getResource("TestSchema.xsd"));
schema = mgr.getSchema("test");
dp = new DocumentPartImpl(schema);
}
@SuppressWarnings("unchecked")
protected static void clearMap(Map<String, Serializable> map) {
Iterator<Map.Entry<String, Serializable>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Serializable> entry = it.next();
Serializable v = entry.getValue();
if (v == null) {
it.remove();
} else if (v instanceof Map) {
clearMap((Map<String, Serializable>) v);
} else if (v instanceof List) {
for (Serializable el : (List<Serializable>) v) {
if (el instanceof Map) {
clearMap((Map<String, Serializable>) el);
}
}
}
}
}
@SuppressWarnings("unchecked")
protected static boolean valueEquals(Object o1, Object o2) {
if (o1 == null && o2 == null) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
if (o1 instanceof Map) {
if (!(o2 instanceof Map)) {
return false;
}
Map<String, Serializable> map1 = (Map<String, Serializable>) o1;
Map<String, Serializable> map2 = (Map<String, Serializable>) o2;
if (map1.size() != map2.size()) {
return false;
}
for (String key : map1.keySet()) {
if (!valueEquals(map1.get(key), map2.get(key))) {
return false;
}
}
} else if (o1 instanceof List) {
if (!(o2 instanceof List)) {
return false;
}
List<Serializable> list1 = (List<Serializable>) o1;
List<Serializable> list2 = (List<Serializable>) o2;
if (list1.size() != list2.size()) {
return false;
}
for (int i = 0; i < list1.size(); i++) {
if (!valueEquals(list1.get(i), list2.get(i))) {
return false;
}
}
} else if (!o1.equals(o2)) {
return false;
}
return true;
}
// Duplicated from NXRuntimeTestCase
public static URL getResource(String resource) {
return Thread.currentThread().getContextClassLoader().getResource(
resource);
}
public void testPath() throws Exception {
String path = dp.get("file").get("fileName").getPath();
assertEquals("/book:file/fileName", path);
Author author = new Author();
dp.get("authors").addValue(author.getMap());
path = dp.resolvePath("book:authors/author[0]/name/firstName").getPath();
assertEquals("/book:authors/author[0]/name/firstName", path);
}
public void testPropertyAccess() throws Exception {
// test complex property access
Property prop = dp.get("title");
assertTrue(prop.isScalar());
assertTrue(prop.isPhantom());
assertNull(prop.getValue());
prop = dp.get("price");
assertTrue(prop.isScalar());
assertTrue(prop.isPhantom());
assertEquals(111L, prop.getValue());
Property authorsProp = dp.get("authors");
assertTrue(authorsProp.isList());
assertTrue(authorsProp.isPhantom());
Property keysProp = dp.get("keywords");
assertTrue(keysProp.isList());
assertTrue(keysProp.isPhantom());
Property refsProp = dp.get("references");
assertTrue(refsProp.isList());
assertTrue(refsProp.isPhantom());
prop = dp.get("creationDate");
assertTrue(prop.isScalar());
assertTrue(prop.isPhantom());
assertNull(prop.getValue());
Property fileProp = dp.get("file");
assertTrue(fileProp.isComplex());
assertTrue(fileProp.isPhantom());
Property fileNameProp = fileProp.get("fileName");
assertTrue(fileNameProp.isComplex());
assertTrue(fileNameProp.isPhantom());
prop = fileNameProp.get("name");
assertTrue(prop.isScalar());
assertTrue(prop.isPhantom());
assertNull(prop.getValue());
prop = fileNameProp.get("extension");
assertTrue(prop.isScalar());
assertTrue(prop.isPhantom());
assertNull(prop.getValue());
// test getRoot
assertEquals(dp, prop.getRoot());
// test raw values
Map<String, Serializable> expected = new Book().getMap();
expected.put("book:price", 111L);
assertTrue(valueEquals(expected, dp.getValue()));
// test resolve path
prop = dp.resolvePath("title");
assertEquals(prop, dp.get("title"));
assertEquals(fileNameProp, dp.resolvePath("file/fileName"));
assertEquals(fileNameProp.get("name"),
dp.resolvePath("file/fileName/name"));
assertEquals(fileNameProp.get("name"), fileNameProp.resolvePath("name"));
assertEquals(fileNameProp.get("name"),
fileNameProp.resolvePath("../fileName/name"));
assertEquals(dp, fileNameProp.resolvePath("../.."));
assertEquals(dp.get("title"), fileNameProp.resolvePath("../../title"));
// using prefixed names
assertEquals(fileNameProp.resolvePath("name/../extension"),
dp.resolvePath("book:file/fileName/extension"));
// testing list access - for this we need a phantom property??
assertEquals(keysProp, dp.resolvePath("book:keywords"));
// testing list access - for this we need a phantom property??
prop = refsProp.addValue("key1");
assertEquals(prop, dp.resolvePath("book:references/reference[0]"));
prop = authorsProp.addValue(new Author().getMap());
assertEquals(prop, dp.resolvePath("authors/author[0]"));
prop = prop.get("name").get("firstName");
assertEquals(prop, dp.resolvePath("authors/author[0]/name/firstName"));
// absolute paths
assertEquals(prop.resolvePath("/book:title"), dp.get("title"));
}
public void testPropertyValueAccess() throws Exception {
// test setters
Property prop = dp.get("title");
prop.setValue("The title");
assertEquals("The title", prop.getValue());
dp.setValue("title", "The title 2");
assertEquals("The title 2", prop.getValue());
dp.setValue("file/fileName/extension", "jpg");
assertEquals("jpg", dp.getValue("book:file/fileName/extension"));
Author author = new Author();
author.age = 100L;
author.name = new Name();
author.name.firstName = "Toto";
prop = dp.get("authors").addValue(author.getMap());
assertEquals(prop.getValue(), author.getMap());
assertEquals("Toto", prop.resolvePath("name/firstName").getValue());
assertNull(prop.get("name").get("lastName").getValue());
// test set(index)
author.name.firstName = null;
author.name.lastName = "Titi";
dp.get("authors").setValue(0, author.getMap());
assertEquals("Titi", prop.resolvePath("name/lastName").getValue());
assertNull(prop.get("name").get("firstName").getValue());
// set using xpath
dp.setValue("authors/author[0]/name/lastName", "Tete");
assertEquals("Tete", dp.getValue("authors/author[0]/name/lastName"));
// test add(index)
author.name.lastName = "Toto";
dp.get("authors").addValue(0, author.getMap());
assertEquals("Toto",
dp.resolvePath("authors/author[0]/name/lastName").getValue());
assertEquals("Tete",
dp.resolvePath("authors/author[1]/name/lastName").getValue());
}
public void testReadOnlyValue() throws Exception {
Property prop = dp.resolvePath("file/fileName/extension");
try {
prop.setReadOnly(true);
prop.setValue("test");
fail();
} catch (ReadOnlyPropertyException e) {
// do nothing
}
}
public void testFlags() throws Exception {
dp.setValue("file/fileName/extension", "ejb");
assertTrue(dp.get("file").isDirty());
assertTrue(dp.get("file").get("fileName").isDirty());
assertTrue(dp.get("file").get("fileName").get("extension").isDirty());
assertTrue(dp.get("file").get("fileName").get("name").isPhantom());
assertEquals("ejb", dp.getValue("file/fileName/extension"));
assertFalse(dp.get("file").isPhantom());
assertFalse(dp.get("file").get("fileName").isPhantom());
assertFalse(dp.get("file").get("fileName").get("extension").isPhantom());
}
public void testDefaultFactories() throws Exception {
Book book = new Book();
Author author = new Author();
author.name.firstName = "John";
book.authors = new ArrayList<Author>();
book.authors.add(author);
book.title = "My Title";
book.creationDate = Calendar.getInstance();
book.price = 100L;
BlobFile file = new BlobFile();
file.fileName.extension = "xml";
book.file = file;
book.keywords = new String[] { "a", "b" };
dp.setValue(book.getMap());
assertTrue(dp.get("book:title") instanceof StringProperty);
assertTrue(dp.get("book:price") instanceof LongProperty);
assertTrue(dp.get("book:creationDate") instanceof DateProperty);
assertTrue(dp.get("book:authors") instanceof ListProperty);
assertTrue(dp.get("book:file") instanceof MapProperty);
}
/**
* Compatibility test - this should be removed when ListDiff will be no more
* used in nuxeo.
*/
public void testListDiffCompatibility() throws Exception {
Book book = new Book();
book.authors = new ArrayList<Author>();
book.authors.add(new Author(1));
book.authors.add(new Author(2));
book.authors.add(new Author(3));
book.authors.add(new Author(4));
book.authors.add(new Author(5));
dp.setValue(book.getMap());
Property prop = dp.get("authors");
ListDiff ld = new ListDiff();
ld.add(new Author(6).getMap()); // 123456
ld.insert(4, new Author(7).getMap()); // 1234756
ld.move(0, 1); // 2134756
ld.remove(2); // 214756
prop.setValue(ld);
book.authors.add(new Author(6));
book.authors.add(4, new Author(7));
Author a = book.authors.get(0);
book.authors.set(0, book.authors.get(1));
book.authors.set(1, a);
book.authors.remove(2);
ArrayList<Serializable> authors = new ArrayList<Serializable>();
for (Author author : book.authors) {
authors.add(author.getMap());
}
assertEquals(authors, prop.getValue());
}
/**
* Compatibility test - this should be removed when ListDiff will be no more
* used in nuxeo.
*/
public void testListDiffCompatibilityForScalarList() throws Exception {
ArrayList<String> references = arrayList("a", "b", "c", "d", "e");
dp.get("references").init(references);
Property prop = dp.get("references");
ListDiff ld = new ListDiff();
ld.add("f"); // abcdef
ld.insert(4, "g"); // abcdgef
ld.move(0, 1); // bacdgef
ld.remove(2); // badgef
prop.setValue(ld);
List<?> list = prop.getValue(List.class);
assertEquals(arrayList("b", "a", "d", "g", "e", "f"), list);
}
public void testScalarList() throws Exception {
ArrayList<String> references = arrayList("a", "b", "c", "d", "e");
dp.get("references").init(references);
Property prop = dp.get("references");
prop.setValue(references);
assertEquals(references, prop.getValue(List.class));
assertEquals(references, prop.getValue());
// FIXME: NXP-1994: New property model makes list properties return null
// values instead of empty list
// setting an empty list at initialization time should not give null at
// fetching time
// ArrayList<String> emptyList = arrayList();
// dp.get("references").init(emptyList);
//
// prop = dp.get("references");
// prop.setValue(emptyList);
//
// assertEquals(emptyList, prop.getValue(List.class));
// assertEquals(emptyList, prop.getValue());
}
public void testBlob() throws Exception {
Book book = new Book();
BlobFile file = new BlobFile();
file.fileName.extension = "xml";
file.blob = new StringBlob("abcdef", "plain/text", "UTF8");
book.file = file;
dp.setValue(book.getMap());
Property pblob = dp.get("file").get("blob");
assertEquals(file.blob, pblob.getValue());
assertEquals(file.blob.getEncoding(), pblob.getValue("encoding"));
assertEquals(file.blob.getMimeType(), pblob.getValue("mime-type"));
StringBlob blob = new StringBlob("xyz", "text/html", "UTF16");
pblob.setValue(blob);
assertEquals(blob.getEncoding(), pblob.getValue("encoding"));
assertEquals(blob.getMimeType(), pblob.getValue("mime-type"));
// TODO
// Map<String, Object> map = new HashMap<String, Object>();
// map.put("encoding", "LATIN2");
// map.put("mime-type", "test/plain");
// map.put("length", 123);
// map.put("data", new ByteArrayInputStream("abc".getBytes()));
// pblob.setValue(map);
// assertEquals("LATIN2", pblob.getValue("encoding"));
}
public void testSerialization() throws Exception {
Book book = new Book();
BlobFile file = new BlobFile();
file.fileName.extension = "xml";
file.blob = new StringBlob("abcdef", "plain/text", "UTF8");
book.file = file;
dp.setValue(book.getMap());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(dp);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(
baos.toByteArray()));
DocumentPartImpl dp2 = (DocumentPartImpl) in.readObject();
// blobs are equals only if they are the same object so we need
// to remove them before doing the assertion
Blob blob1 = (Blob) dp.get("file").get("blob").remove();
Blob blob2 = (Blob) dp2.get("file").get("blob").remove();
// remove array also for the same reason as the blob
Object[] keywords1 = (Object[]) dp.get("keywords").remove();
Object[] keywords2 = (Object[]) dp2.get("keywords").remove();
ArrayList<?> references1 = (ArrayList<?>) dp.get("references").remove();
ArrayList<?> references2 = (ArrayList<?>) dp2.get("references").remove();
assertEquals(dp2.getValue(), dp.getValue());
// now check the blobs they are equals
assertEquals(blob1.getEncoding(), blob2.getEncoding());
assertEquals(blob1.getMimeType(), blob2.getMimeType());
assertEquals(blob1.getLength(), blob2.getLength());
assertEquals(blob1.getString(), blob2.getString());
// now check arrays
assertTrue(Arrays.equals(keywords1, keywords2));
assertEquals(references1, references2);
}
@SuppressWarnings("unchecked")
public void testDirtyChildren() throws Exception {
Iterator<Property> it = dp.getDirtyChildren();
assertFalse(it.hasNext());
Book book = new Book();
Author author = new Author();
author.name.firstName = "John";
book.authors = new ArrayList<Author>();
book.authors.add(author);
book.title = "My Title";
Map<String, Serializable> map = book.getMap();
map.remove("book:price");
map.remove("book:creationDate");
map.remove("book:references");
map.remove("book:keywords");
map.remove("book:file");
List<Serializable> list = (List<Serializable>) map.get("book:authors");
Map<String, Serializable> amap = (Map<String, Serializable>) list.get(0);
amap.remove("age");
amap = (Map<String, Serializable>) amap.get("name");
amap.remove("lastName");
dp.setValue(map);
it = dp.getDirtyChildren();
List<Property> properties = new ArrayList<Property>();
while (it.hasNext()) {
properties.add(it.next());
}
assertTrue(properties.contains(dp.resolvePath("title")));
assertTrue(properties.contains(dp.resolvePath("authors")));
it = dp.get("authors").getDirtyChildren();
Property p = it.next();
assertFalse(it.hasNext());
it = p.getDirtyChildren();
p = it.next();
assertFalse(it.hasNext());
it = p.getDirtyChildren();
p = it.next();
assertEquals("John", p.getValue());
assertFalse(it.hasNext());
try {
p.getDirtyChildren();
fail("scalar property cannot have children");
} catch (UnsupportedOperationException e) {
// do nothing
}
}
@SuppressWarnings("unchecked")
public void testInit() throws Exception {
Book book = new Book();
Author author = new Author();
author.name.firstName = "John";
book.authors = new ArrayList<Author>();
book.authors.add(author);
book.title = "My Title";
BlobFile file = new BlobFile();
file.fileName.extension = "xml";
book.file = file;
book.keywords = new String[] { "a", "b" };
book.references = arrayList("a", "b");
HashMap<String, Serializable> map = book.getMap();
// remove name so that it will be a phantom
Map<String, Serializable> map2 = (Map<String, Serializable>) map.get("book:file");
Map<String, Serializable> map3 = (Map<String, Serializable>) map2.get("fileName");
map3.remove("name");
dp.init(map);
assertFalse(dp.isDirty());
assertFalse(dp.get("file").isDirty());
assertFalse(dp.get("file").get("fileName").isDirty());
assertFalse(dp.get("file").get("fileName").get("extension").isDirty());
assertFalse(dp.get("file").get("fileName").get("name").isDirty());
assertFalse(dp.get("file").isPhantom());
assertFalse(dp.get("file").get("fileName").isPhantom());
assertFalse(dp.get("file").get("fileName").get("extension").isPhantom());
assertFalse(dp.resolvePath("authors/auhtor[0]/name/firstName").isPhantom());
assertTrue(dp.get("file").get("fileName").get("name").isPhantom());
assertEquals("xml", dp.getValue("file/fileName/extension"));
assertEquals("My Title", dp.getValue("title"));
assertEquals("John", dp.getValue("authors/author[0]/name/firstName"));
Object[] ar = (Object[]) dp.getValue("keywords");
assertEquals("a", ar[0]);
assertEquals("b", ar[1]);
assertEquals("a", dp.getValue("references/reference[0]"));
assertEquals("b", dp.getValue("references/reference[1]"));
assertEquals(111L, dp.getValue("price"));
assertNull(dp.getValue("authors/author[0]/name/lastName"));
// test list size
assertEquals(1, dp.get("authors").size());
assertEquals(2, ar.length);
assertEquals(2, dp.get("references").size());
}
public void testExport() throws Exception {
Book book = new Book();
Author author = new Author();
author.name.firstName = "John";
book.authors = new ArrayList<Author>();
book.authors.add(author);
book.title = "My Title";
BlobFile file = new BlobFile();
file.fileName.extension = "xml";
book.file = file;
book.keywords = new String[] { "a", "b" };
HashMap<String, Serializable> map = book.getMap();
// remove name so that it will be a phantom
((Map) ((Map) map.get("book:file")).get("fileName")).remove("name");
// remove null values - since they are related to phantom props
clearMap(map);
dp.init(map);
// double s = System.currentTimeMillis();
ValueExporter me = new ValueExporter();
Map<String, Serializable> export = me.run(dp);
// double e = System.currentTimeMillis();
// System.out.println("#########visitor >>>> "+((e-s)/1000));
assertEquals(map, export);
}
}