Commit 1f070783 authored by Ate Douma's avatar Ate Douma

CMS7-7024: splitting up ContentTypeField into separate ContentTypeProperty and ContentTypeChild

- now allows same named child/property for derived ContentTypes, but not for non-derived/explicit defined ContentTypes
- also add support for (but only when derived) multi-typed property/child items, e.g. properties or children with the same name but different type
- fixed and renamed several methods with more appropriate naming
parent 8302915f
/*
* Copyright 2013 Hippo B.V. (http://www.onehippo.com)
*
* 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.
*/
package org.onehippo.cms7.services.contenttype;
public class ContentTypeChildImpl extends ContentTypeItemImpl implements ContentTypeChild {
public ContentTypeChildImpl(String definingType, String name, String itemType) {
super(definingType, name, itemType);
}
public ContentTypeChildImpl(EffectiveNodeTypeChild child) {
super(child);
}
public ContentTypeChildImpl(ContentTypeChildImpl other) {
super(other);
}
@Override
public EffectiveNodeTypeChild getEffectiveNodeTypeItem() {
return (EffectiveNodeTypeChild)super.getEffectiveNodeTypeItem();
}
}
...@@ -22,58 +22,64 @@ import java.util.HashMap; ...@@ -22,58 +22,64 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class ContentTypeFieldImpl extends Sealable implements ContentTypeField { public class ContentTypeItemImpl extends Sealable implements ContentTypeItem {
private final String definingType; private final String definingType;
private final String name; private final String name;
private EffectiveNodeTypeItem nti; private EffectiveNodeTypeItem nti;
private final boolean propertyField; private final boolean property;
private boolean derivedField; private boolean derivedItem;
private final String fieldType;
private final String itemType; private final String itemType;
private boolean primaryField; private final String effectiveType;
private boolean primaryItem;
private boolean multiple; private boolean multiple;
private boolean mandatory; private boolean mandatory;
private boolean autoCreated; private boolean autoCreated;
private boolean protect; private boolean protect;
private boolean ordered; private boolean ordered;
private boolean multiTyped;
private List<EffectiveNodeTypeItem> multiTypes = Collections.emptyList();
private List<String> validators = new ArrayList<String>(); private List<String> validators = new ArrayList<String>();
private Map<String, List<String>> fieldProperties = new HashMap<String, List<String>>(); private Map<String, List<String>> itemProperties = new HashMap<String, List<String>>();
@Override @Override
protected void doSeal() { protected void doSeal() {
validators = Collections.unmodifiableList(validators); validators = Collections.unmodifiableList(validators);
for (Map.Entry<String,List<String>> entry : fieldProperties.entrySet()) { for (Map.Entry<String,List<String>> entry : itemProperties.entrySet()) {
entry.setValue(Collections.unmodifiableList(entry.getValue())); entry.setValue(Collections.unmodifiableList(entry.getValue()));
} }
fieldProperties = Collections.unmodifiableMap(fieldProperties); for (EffectiveNodeTypeItem item : multiTypes) {
((Sealable)item).seal();
}
itemProperties = Collections.unmodifiableMap(itemProperties);
} }
public ContentTypeFieldImpl(String definingType, String name, String fieldType, String itemType) { protected ContentTypeItemImpl(String definingType, String name, String itemType, String effectiveType) {
this.definingType = definingType; this.definingType = definingType;
this.name = name; this.name = name;
this.fieldType = fieldType;
this.itemType = itemType; this.itemType = itemType;
this.propertyField = true; this.effectiveType = effectiveType;
this.property = true;
} }
public ContentTypeFieldImpl(String definingType, String name, String fieldType) { protected ContentTypeItemImpl(String definingType, String name, String itemType) {
this.definingType = definingType; this.definingType = definingType;
this.name = name; this.name = name;
this.fieldType = fieldType; this.itemType = itemType;
this.itemType = fieldType; this.effectiveType = itemType;
this.propertyField = false; this.property = false;
} }
public ContentTypeFieldImpl(EffectiveNodeTypeProperty property) { protected ContentTypeItemImpl(EffectiveNodeTypeProperty property) {
this.definingType = property.getDefiningType(); this.definingType = property.getDefiningType();
this.nti = property; this.nti = property;
this.primaryField = false; this.primaryItem = false;
this.propertyField = true; this.property = true;
this.derivedField = true; this.derivedItem = true;
this.name = property.getName(); this.name = property.getName();
this.itemType = property.getType(); this.effectiveType = property.getType();
this.fieldType = this.itemType; this.itemType = this.effectiveType;
this.multiple = property.isMultiple(); this.multiple = property.isMultiple();
this.mandatory = property.isMandatory(); this.mandatory = property.isMandatory();
this.autoCreated = property.isAutoCreated(); this.autoCreated = property.isAutoCreated();
...@@ -81,15 +87,15 @@ public class ContentTypeFieldImpl extends Sealable implements ContentTypeField { ...@@ -81,15 +87,15 @@ public class ContentTypeFieldImpl extends Sealable implements ContentTypeField {
this.ordered = false; this.ordered = false;
} }
public ContentTypeFieldImpl(EffectiveNodeTypeChild child) { protected ContentTypeItemImpl(EffectiveNodeTypeChild child) {
this.definingType = child.getDefiningType(); this.definingType = child.getDefiningType();
this.nti = child; this.nti = child;
this.primaryField = false; this.primaryItem = false;
this.propertyField = false; this.property = false;
this.derivedField = true; this.derivedItem = true;
this.name = child.getName(); this.name = child.getName();
this.itemType = child.getType(); this.effectiveType = child.getType();
this.fieldType = this.itemType; this.itemType = this.effectiveType;
this.multiple = child.isMultiple(); this.multiple = child.isMultiple();
this.mandatory = child.isMandatory(); this.mandatory = child.isMandatory();
this.autoCreated = child.isAutoCreated(); this.autoCreated = child.isAutoCreated();
...@@ -97,22 +103,22 @@ public class ContentTypeFieldImpl extends Sealable implements ContentTypeField { ...@@ -97,22 +103,22 @@ public class ContentTypeFieldImpl extends Sealable implements ContentTypeField {
this.ordered = false; this.ordered = false;
} }
public ContentTypeFieldImpl(ContentTypeFieldImpl other) { protected ContentTypeItemImpl(ContentTypeItemImpl other) {
this.definingType = other.definingType; this.definingType = other.definingType;
this.nti = other.nti; this.nti = other.nti;
this.primaryField = other.primaryField; this.primaryItem = other.primaryItem;
this.propertyField = other.propertyField; this.property = other.property;
this.derivedField = other.derivedField; this.derivedItem = other.derivedItem;
this.name = other.name; this.name = other.name;
this.effectiveType = other.effectiveType;
this.itemType = other.itemType; this.itemType = other.itemType;
this.fieldType = other.fieldType;
this.multiple = other.multiple; this.multiple = other.multiple;
this.mandatory = other.mandatory; this.mandatory = other.mandatory;
this.autoCreated = other.autoCreated; this.autoCreated = other.autoCreated;
this.protect = other.protect; this.protect = other.protect;
this.ordered = other.ordered; this.ordered = other.ordered;
this.validators.addAll(other.validators); this.validators.addAll(other.validators);
this.fieldProperties.putAll(other.fieldProperties); this.itemProperties.putAll(other.itemProperties);
} }
@Override @Override
...@@ -136,33 +142,33 @@ public class ContentTypeFieldImpl extends Sealable implements ContentTypeField { ...@@ -136,33 +142,33 @@ public class ContentTypeFieldImpl extends Sealable implements ContentTypeField {
} }
@Override @Override
public boolean isPropertyField() { public boolean isProperty() {
return propertyField; return property;
} }
@Override @Override
public boolean isDerivedField() { public boolean isDerivedItem() {
return derivedField; return derivedItem;
} }
@Override @Override
public String getFieldType() { public String getItemType() {
return fieldType; return itemType;
} }
@Override @Override
public String getItemType() { public String getEffectiveType() {
return itemType; return effectiveType;
} }
@Override @Override
public boolean isPrimaryField() { public boolean isPrimaryItem() {
return primaryField; return primaryItem;
} }
public void setPrimaryField(boolean primaryField) { public void setPrimaryItem(boolean primaryItem) {
checkSealed(); checkSealed();
this.primaryField = primaryField; this.primaryItem = primaryItem;
} }
@Override @Override
...@@ -221,7 +227,38 @@ public class ContentTypeFieldImpl extends Sealable implements ContentTypeField { ...@@ -221,7 +227,38 @@ public class ContentTypeFieldImpl extends Sealable implements ContentTypeField {
} }
@Override @Override
public Map<String, List<String>> getFieldProperties() { public Map<String, List<String>> getItemProperties() {
return fieldProperties; return itemProperties;
}
@Override
public boolean isMultiTyped() {
return !multiTypes.isEmpty();
}
public List<EffectiveNodeTypeItem> getMultiTypes() {
return multiTypes;
}
public void setMultiPropertyTypes(List<EffectiveNodeTypeProperty> types) {
checkSealed();
if (types != null) {
multiTypes = Collections.unmodifiableList(new ArrayList<EffectiveNodeTypeItem>(types));
}
else {
multiTypes = Collections.emptyList();
}
multiTyped = !multiTypes.isEmpty();
}
public void setMultiChildTypes(List<EffectiveNodeTypeChild> types) {
checkSealed();
if (types != null) {
multiTypes = Collections.unmodifiableList(new ArrayList<EffectiveNodeTypeItem>(types));
}
else {
multiTypes = Collections.emptyList();
}
multiTyped = !multiTypes.isEmpty();
} }
} }
/*
* Copyright 2013 Hippo B.V. (http://www.onehippo.com)
*
* 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.
*/
package org.onehippo.cms7.services.contenttype;
public class ContentTypePropertyImpl extends ContentTypeItemImpl implements ContentTypeProperty {
public ContentTypePropertyImpl(String definingType, String name, String itemType, String effectiveType) {
super(definingType, name, itemType, effectiveType);
}
public ContentTypePropertyImpl(EffectiveNodeTypeProperty property) {
super(property);
}
public ContentTypePropertyImpl(ContentTypePropertyImpl other) {
super(other);
}
@Override
public EffectiveNodeTypeProperty getEffectiveNodeTypeItem() {
return (EffectiveNodeTypeProperty)super.getEffectiveNodeTypeItem();
}
}
...@@ -142,7 +142,7 @@ class ContentTypesCache extends Sealable implements ContentTypes { ...@@ -142,7 +142,7 @@ class ContentTypesCache extends Sealable implements ContentTypes {
these extended field properties needs to have all fields resolved first which complicates the proper moment for sealing these extended field properties needs to have all fields resolved first which complicates the proper moment for sealing
for (String typeName : typeNodes.keySet()) { for (String typeName : typeNodes.keySet()) {
loadContentTypeFieldProperties(types.get(typeName), typeNodes.get(typeName)); loadContentTypeItemProperties(types.get(typeName), typeNodes.get(typeName));
} }
*/ */
...@@ -164,9 +164,9 @@ class ContentTypesCache extends Sealable implements ContentTypes { ...@@ -164,9 +164,9 @@ class ContentTypesCache extends Sealable implements ContentTypes {
resolveContentType(name); resolveContentType(name);
} }
// 5th pass: resolve all ContentTypeFields and seal all types // 5th pass: resolve all ContentTypeItems and seal all types
for (AggregatedContentTypesCache.Key key : actCache.getKeys()) { for (AggregatedContentTypesCache.Key key : actCache.getKeys()) {
resolveContentTypeFieldsAndSeal(actCache.get(key)); resolveContentTypeItemsAndSeal(actCache.get(key));
} }
//lock down the cache itself //lock down the cache itself
...@@ -206,8 +206,8 @@ class ContentTypesCache extends Sealable implements ContentTypes { ...@@ -206,8 +206,8 @@ class ContentTypesCache extends Sealable implements ContentTypes {
if (field.isNodeType(HippoNodeType.NT_FIELD)) { if (field.isNodeType(HippoNodeType.NT_FIELD)) {
ContentTypeFieldImpl ctf; ContentTypeItemImpl cti;
String fieldType; String itemType;
String fieldName = field.getProperty(HippoNodeType.HIPPO_PATH).getString(); String fieldName = field.getProperty(HippoNodeType.HIPPO_PATH).getString();
...@@ -216,38 +216,43 @@ class ContentTypesCache extends Sealable implements ContentTypes { ...@@ -216,38 +216,43 @@ class ContentTypesCache extends Sealable implements ContentTypes {
continue; continue;
} }
fieldType = JcrUtils.getStringProperty(field, HippoNodeType.HIPPOSYSEDIT_TYPE, PropertyType.TYPENAME_STRING); itemType = JcrUtils.getStringProperty(field, HippoNodeType.HIPPOSYSEDIT_TYPE, PropertyType.TYPENAME_STRING);
if (propertyTypeMappings.containsKey(fieldType)) { if (propertyTypeMappings.containsKey(itemType)) {
ctf = new ContentTypeFieldImpl(ct.getName(), fieldName, fieldType, propertyTypeMappings.get(fieldType)); cti = new ContentTypePropertyImpl(ct.getName(), fieldName, itemType, propertyTypeMappings.get(itemType));
} }
else if (types.containsKey(fieldType)) { else if (types.containsKey(itemType)) {
ctf = new ContentTypeFieldImpl(ct.getName(), fieldName, fieldType); cti = new ContentTypeChildImpl(ct.getName(), fieldName, itemType);
} }
else if (entCache.getTypes().containsKey(fieldType)) { else if (entCache.getTypes().containsKey(itemType)) {
ctf = new ContentTypeFieldImpl(ct.getName(), fieldName, fieldType); cti = new ContentTypeChildImpl(ct.getName(), fieldName, itemType);
} }
else { else {
// TODO: log warn unknown fieldType value // TODO: log warn unknown itemType value
continue; continue;
} }
ctf.setMandatory(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_MANDATORY, false)); cti.setMandatory(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_MANDATORY, false));
ctf.setAutoCreated(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_AUTOCREATED, false)); cti.setAutoCreated(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_AUTOCREATED, false));
ctf.setMultiple(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_MULTIPLE, false)); cti.setMultiple(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_MULTIPLE, false));
ctf.setOrdered(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_ORDERED, false)); cti.setOrdered(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_ORDERED, false));
ctf.setProtected(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_PROTECTED, false)); cti.setProtected(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_PROTECTED, false));
ctf.setPrimaryField(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_PRIMARY, false)); cti.setPrimaryItem(JcrUtils.getBooleanProperty(field, HippoNodeType.HIPPO_PRIMARY, false));
if (field.hasProperty(HippoNodeType.HIPPO_VALIDATORS)) { if (field.hasProperty(HippoNodeType.HIPPO_VALIDATORS)) {
Value[] values = field.getProperty(HippoNodeType.HIPPO_VALIDATORS).getValues(); Value[] values = field.getProperty(HippoNodeType.HIPPO_VALIDATORS).getValues();
for (Value value : values) { for (Value value : values) {
String validator = value.getString(); String validator = value.getString();
if (validator.length() > 0) { if (validator.length() > 0) {
ctf.getValidators().add(validator); cti.getValidators().add(validator);
} }
} }
} }
ct.getFields().put(ctf.getName(), ctf); if (cti.isProperty()) {
ct.getProperties().put(cti.getName(), (ContentTypeProperty)cti);
}
else {
ct.getChildren().put(cti.getName(), (ContentTypeChild)cti);
}
} }
} }
} }
...@@ -365,12 +370,12 @@ class ContentTypesCache extends Sealable implements ContentTypes { ...@@ -365,12 +370,12 @@ class ContentTypesCache extends Sealable implements ContentTypes {
return result; return result;
} }
private void resolveContentTypeFieldsAndSeal(ContentTypeImpl ct) { private void resolveContentTypeItemsAndSeal(ContentTypeImpl ct) {
if (!ct.isSealed()) { if (!ct.isSealed()) {
for (String s : ct.getSuperTypes()) { for (String s : ct.getSuperTypes()) {
resolveContentTypeFieldsAndSeal(actCache.get(s)); resolveContentTypeItemsAndSeal(actCache.get(s));
} }
ct.resolveFields(this); ct.resolveItems(this);
ct.seal(); ct.seal();
} }
} }
......
...@@ -173,16 +173,20 @@ public class HippoContentTypeServiceTest extends PluginTest { ...@@ -173,16 +173,20 @@ public class HippoContentTypeServiceTest extends PluginTest {
fail("UnsupportedOperationException expected for ContentTypes.getType(test:test).getSuperTypes().clear()"); fail("UnsupportedOperationException expected for ContentTypes.getType(test:test).getSuperTypes().clear()");
} catch (UnsupportedOperationException uoe) {} } catch (UnsupportedOperationException uoe) {}
try { try {
ctCache.getType("test:test").getFields().clear(); ctCache.getType("test:test").getChildren().clear();
fail("UnsupportedOperationException expected for ContentTypes.getType(test:test).getFields().clear()"); fail("UnsupportedOperationException expected for ContentTypes.getType(test:test).getChildren().clear()");
} catch (UnsupportedOperationException uoe) {} } catch (UnsupportedOperationException uoe) {}
try { try {
ctCache.getType("test:test").getFields().get("test:title").getFieldProperties().clear(); ctCache.getType("test:test").getProperties().clear();
fail("UnsupportedOperationException expected for ContentTypes.getType(test:test).getFields().get(test:title).getFieldProperties().clear()"); fail("UnsupportedOperationException expected for ContentTypes.getType(test:test).getProperties().clear()");
} catch (UnsupportedOperationException uoe) {} } catch (UnsupportedOperationException uoe) {}
try { try {
ctCache.getType("test:test").getFields().get("test:title").getValidators().clear(); ctCache.getType("test:test").getProperties().get("test:title").getItemProperties().clear();
fail("UnsupportedOperationException expected for ContentTypes.getType(test:test).getFields().get(test:title).getValidators().clear()"); fail("UnsupportedOperationException expected for ContentTypes.getType(test:test).getProperties().get(test:title).getItemProperties().clear()");
} catch (UnsupportedOperationException uoe) {}
try {
ctCache.getType("test:test").getItem("test:title").getValidators().clear();
fail("UnsupportedOperationException expected for ContentTypes.getType(test:test).getItem(test:title).getValidators().clear()");
} catch (UnsupportedOperationException uoe) {} } catch (UnsupportedOperationException uoe) {}
// repeat sealed check for EffectiveNodeType underlying the ContentType // repeat sealed check for EffectiveNodeType underlying the ContentType
...@@ -229,7 +233,8 @@ public class HippoContentTypeServiceTest extends PluginTest { ...@@ -229,7 +233,8 @@ public class HippoContentTypeServiceTest extends PluginTest {
ContentType ct = ctCache.getType("test:test"); ContentType ct = ctCache.getType("test:test");
assertNotNull(ct); assertNotNull(ct);
assertEquals(4, ct.getFields().size()); assertEquals(3, ct.getProperties().size());
assertEquals(1, ct.getChildren().size());
assertEquals(1, ct.getAggregatedTypes().size()); assertEquals(1, ct.getAggregatedTypes().size());
assertTrue(!ct.getSuperTypes().contains("hippostd:container")); assertTrue(!ct.getSuperTypes().contains("hippostd:container"));
...@@ -246,8 +251,8 @@ public class HippoContentTypeServiceTest extends PluginTest { ...@@ -246,8 +251,8 @@ public class HippoContentTypeServiceTest extends PluginTest {
ct = ctCache.getType("test:test"); ct = ctCache.getType("test:test");
// added test:extraField shouldn't be merged as there is no matching property in the EffectiveNodeType // added test:extraField shouldn't be merged as there is no matching property in the EffectiveNodeType
assertEquals(4, ct.getFields().size()); assertEquals(3, ct.getProperties().size());
assertTrue(!ct.getFields().containsKey("test:extraField")); assertTrue(!ct.getProperties().containsKey("test:extraField"));
// adding relaxed mixin should expose and 'enable' the extraField // adding relaxed mixin should expose and 'enable' the extraField
...@@ -259,10 +264,10 @@ public class HippoContentTypeServiceTest extends PluginTest { ...@@ -259,10 +264,10 @@ public class HippoContentTypeServiceTest extends PluginTest {
ctCache = service.getContentTypes(); ctCache = service.getContentTypes();
ct = ctCache.getType("test:test"); ct = ctCache.getType("test:test");
assertEquals(5, ct.getFields().size()); assertEquals(4, ct.getProperties().size());
assertTrue(ct.getFields().containsKey("test:extraField")); assertTrue(ct.getProperties().containsKey("test:extraField"));
assertTrue(ct.getFields().get("test:extraField").getEffectiveNodeTypeItem().getName().equals("*")); assertTrue(ct.getItem("test:extraField").getEffectiveNodeTypeItem().getName().equals("*"));
assertTrue(ct.getFields().get("test:extraField").getEffectiveNodeTypeItem().getDefiningType().equals("hippostd:relaxed")); assertTrue(ct.getItem("test:extraField").getEffectiveNodeTypeItem().getDefiningType().equals("hippostd:relaxed"));
assertTrue(ct.getAggregatedTypes().contains("hippostd:relaxed")); assertTrue(ct.getAggregatedTypes().contains("hippostd:relaxed"));
assertTrue(ct.getSuperTypes().contains("hippostd:container")); assertTrue(ct.getSuperTypes().contains("hippostd:container"));
...@@ -271,15 +276,16 @@ public class HippoContentTypeServiceTest extends PluginTest { ...@@ -271,15 +276,16 @@ public class HippoContentTypeServiceTest extends PluginTest {
ct = service.getContentTypes().getContentTypeForNodeByPath(session, "/testNode"); ct = service.getContentTypes().getContentTypeForNodeByPath(session, "/testNode");
assertEquals(4, ct.getFields().size()); assertEquals(3, ct.getProperties().size());
assertEquals(1, ct.getChildren().size());
assertEquals(1, ct.getAggregatedTypes().size()); assertEquals(1, ct.getAggregatedTypes().size());
session.getNode("/testNode").addMixin("hippostd:relaxed"); session.getNode("/testNode").addMixin("hippostd:relaxed");
session.save(); session.save();
ct = service.getContentTypes().getContentTypeForNodeByPath(session, "/testNode"); ct = service.getContentTypes().getContentTypeForNodeByPath(session, "/testNode");
assertEquals(5, ct.getFields().size()); assertEquals(4, ct.getProperties().size());
assertTrue(ct.getFields().containsKey("test:extraField")); assertTrue(ct.getProperties().containsKey("test:extraField"));
assertTrue(ct.getAggregatedTypes().contains("hippostd:relaxed")); assertTrue(ct.getAggregatedTypes().contains("hippostd:relaxed"));
assertTrue(ct.getSuperTypes().contains("hippostd:container")); assertTrue(ct.getSuperTypes().contains("hippostd:container"));
......
...@@ -10,23 +10,24 @@ ...@@ -10,23 +10,24 @@
"mixin" : false, "mixin" : false,
"templateType" : false, "templateType" : false,
"cascadeValidate" : false, "cascadeValidate" : false,
"fields" : { "properties" : {
"test:title" : { "test:title" : {
"definingType" : "test:test", "definingType" : "test:test",
"name" : "test:title", "name" : "test:title",
"propertyField" : true, "property" : true,
"derivedField" : false, "derivedItem" : false,
"fieldType" : "String",
"itemType" : "String", "itemType" : "String",
"primaryField" : false, "effectiveType" : "String",
"primaryItem" : false,
"multiple" : false, "multiple" : false,
"mandatory" : false, "mandatory" : false,
"autoCreated" : false, "autoCreated" : false,
"ordered" : false, "ordered" : false,
"multiTyped" : false,
"multiTypes" : [ ],
"validators" : [ ], "validators" : [ ],
"fieldProperties" : { "itemProperties" : {
}, },
"protected" : false,
"effectiveNodeTypeItem" : { "effectiveNodeTypeItem" : {
"name" : "test:title", "name" : "test:title",
"definingType" : "test:test", "definingType" : "test:test",
...@@ -40,54 +41,26 @@ ...@@ -40,54 +41,26 @@
"defaultValues" : [ ],