Java tutorial
/** * Copyright (C) 2010-2016 Structr GmbH * * This file is part of Structr <http://structr.org>. * * Structr is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Structr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Structr. If not, see <http://www.gnu.org/licenses/>. */ package org.structr.core.entity; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.neo4j.helpers.Predicate; import org.neo4j.helpers.collection.Iterables; import org.structr.common.CaseHelper; import org.structr.common.PermissionPropagation; import org.structr.common.PropertyView; import org.structr.common.SecurityContext; import org.structr.common.ValidationHelper; import org.structr.common.View; import org.structr.common.error.ErrorBuffer; import org.structr.common.error.FrameworkException; import org.structr.core.GraphObject; import org.structr.core.entity.relationship.Ownership; import org.structr.core.entity.relationship.SchemaRelationshipSourceNode; import org.structr.core.entity.relationship.SchemaRelationshipTargetNode; import org.structr.core.graph.TransactionCommand; import org.structr.core.notion.PropertyNotion; import org.structr.core.property.EndNode; import org.structr.core.property.EntityNotionProperty; import org.structr.core.property.EnumProperty; import org.structr.core.property.LongProperty; import org.structr.core.property.Property; import org.structr.core.property.PropertyKey; import org.structr.core.property.PropertyMap; import org.structr.core.property.StartNode; import org.structr.core.property.StringProperty; import org.structr.schema.ReloadSchema; import org.structr.schema.SchemaHelper; import org.structr.schema.SchemaHelper.Type; import org.structr.schema.action.ActionEntry; import org.structr.schema.action.Actions; import org.structr.schema.json.JsonSchema; import org.structr.schema.json.JsonSchema.Cascade; import org.structr.schema.parser.Validator; /** * * */ public class SchemaRelationshipNode extends AbstractSchemaNode { private static final Logger logger = Logger.getLogger(SchemaRelationshipNode.class.getName()); private static final Set<String> propagatingRelTypes = new TreeSet<>(); private static final Pattern ValidKeyPattern = Pattern.compile("[a-zA-Z_]+"); public static final Property<SchemaNode> sourceNode = new StartNode<>("sourceNode", SchemaRelationshipSourceNode.class); public static final Property<SchemaNode> targetNode = new EndNode<>("targetNode", SchemaRelationshipTargetNode.class); public static final Property<String> sourceId = new EntityNotionProperty<>("sourceId", sourceNode, new PropertyNotion(GraphObject.id)); public static final Property<String> targetId = new EntityNotionProperty<>("targetId", targetNode, new PropertyNotion(GraphObject.id)); public static final Property<String> name = new StringProperty("name").indexed(); public static final Property<String> relationshipType = new StringProperty("relationshipType").indexed(); public static final Property<String> sourceMultiplicity = new StringProperty("sourceMultiplicity"); public static final Property<String> targetMultiplicity = new StringProperty("targetMultiplicity"); public static final Property<String> sourceNotion = new StringProperty("sourceNotion"); public static final Property<String> targetNotion = new StringProperty("targetNotion"); public static final Property<String> sourceJsonName = new StringProperty("sourceJsonName"); public static final Property<String> targetJsonName = new StringProperty("targetJsonName"); public static final Property<String> previousSourceJsonName = new StringProperty("oldSourceJsonName"); public static final Property<String> previousTargetJsonName = new StringProperty("oldTargetJsonName"); public static final Property<String> extendsClass = new StringProperty("extendsClass").indexed(); public static final Property<Long> cascadingDeleteFlag = new LongProperty("cascadingDeleteFlag"); public static final Property<Long> autocreationFlag = new LongProperty("autocreationFlag"); public enum Propagation { Add, Keep, Remove } public enum Direction { None, In, Out, Both } // permission propagation via domain relationships public static final Property<Direction> permissionPropagation = new EnumProperty("permissionPropagation", Direction.class, Direction.None); public static final Property<Propagation> readPropagation = new EnumProperty<>("readPropagation", Propagation.class, Propagation.Remove); public static final Property<Propagation> writePropagation = new EnumProperty<>("writePropagation", Propagation.class, Propagation.Remove); public static final Property<Propagation> deletePropagation = new EnumProperty<>("deletePropagation", Propagation.class, Propagation.Remove); public static final Property<Propagation> accessControlPropagation = new EnumProperty<>( "accessControlPropagation", Propagation.class, Propagation.Remove); public static final Property<String> propertyMask = new StringProperty("propertyMask"); public static final View defaultView = new View(SchemaRelationshipNode.class, PropertyView.Public, name, sourceId, targetId, sourceMultiplicity, targetMultiplicity, sourceNotion, targetNotion, relationshipType, sourceJsonName, targetJsonName, extendsClass, cascadingDeleteFlag, autocreationFlag, previousSourceJsonName, previousTargetJsonName, permissionPropagation, readPropagation, writePropagation, deletePropagation, accessControlPropagation, propertyMask); public static final View uiView = new View(SchemaRelationshipNode.class, PropertyView.Ui, name, sourceId, targetId, sourceMultiplicity, targetMultiplicity, sourceNotion, targetNotion, relationshipType, sourceJsonName, targetJsonName, extendsClass, cascadingDeleteFlag, autocreationFlag, permissionPropagation, readPropagation, writePropagation, deletePropagation, accessControlPropagation, propertyMask); public static final View exportView = new View(SchemaMethod.class, "export", sourceId, targetId, sourceMultiplicity, targetMultiplicity, sourceNotion, targetNotion, relationshipType, sourceJsonName, targetJsonName, extendsClass, cascadingDeleteFlag, autocreationFlag, permissionPropagation, propertyMask); private final Set<String> dynamicViews = new LinkedHashSet<>(); public static void registerPropagatingRelationshipType(final String type) { propagatingRelTypes.add(type); } public static void clearPropagatingRelationshipTypes() { propagatingRelTypes.clear(); } public static Set<String> getPropagatingRelationshipTypes() { return propagatingRelTypes; } @Override public Iterable<PropertyKey> getPropertyKeys(final String propertyView) { final Set<PropertyKey> propertyKeys = new LinkedHashSet<>( Iterables.toList(super.getPropertyKeys(propertyView))); // add "custom" property keys as String properties for (final String key : SchemaHelper.getProperties(getNode())) { final PropertyKey newKey = new StringProperty(key); newKey.setDeclaringClass(getClass()); propertyKeys.add(newKey); } return propertyKeys; } @Override public boolean isValid(final ErrorBuffer errorBuffer) { boolean error = false; error |= ValidationHelper.checkStringNotBlank(this, relationshipType, errorBuffer); return !error && super.isValid(errorBuffer); } @Override public boolean onCreation(SecurityContext securityContext, final ErrorBuffer errorBuffer) throws FrameworkException { if (super.onCreation(securityContext, errorBuffer)) { // store old property names setProperty(previousSourceJsonName, getProperty(sourceJsonName)); setProperty(previousTargetJsonName, getProperty(targetJsonName)); // register transaction post processing that recreates the schema information TransactionCommand.postProcess("reloadSchema", new ReloadSchema()); return true; } return false; } @Override public boolean onModification(SecurityContext securityContext, final ErrorBuffer errorBuffer) throws FrameworkException { if (super.onModification(securityContext, errorBuffer)) { checkClassName(); checkAndRenameSourceAndTargetJsonNames(); // store old property names setProperty(previousSourceJsonName, getProperty(sourceJsonName)); setProperty(previousTargetJsonName, getProperty(targetJsonName)); // register transaction post processing that recreates the schema information TransactionCommand.postProcess("reloadSchema", new ReloadSchema()); return true; } return false; } @Override public boolean onDeletion(SecurityContext securityContext, ErrorBuffer errorBuffer, PropertyMap properties) throws FrameworkException { if (super.onDeletion(securityContext, errorBuffer, properties)) { removeSourceAndTargetJsonNames(properties); // register transaction post processing that recreates the schema information TransactionCommand.postProcess("reloadSchema", new ReloadSchema()); return true; } return false; } public SchemaNode getSourceNode() { return getProperty(sourceNode); } public SchemaNode getTargetNode() { return getProperty(targetNode); } // ----- interface Schema ----- @Override public String getClassName() { String name = getProperty(AbstractNode.name); if (name == null) { final String _sourceType = getSchemaNodeSourceType(); final String _targetType = getSchemaNodeTargetType(); final String _relType = SchemaHelper.cleanPropertyName(getRelationshipType()); name = _sourceType + _relType + _targetType; try { setProperty(AbstractNode.name, name); } catch (FrameworkException fex) { logger.log(Level.WARNING, "Unable to set relationship name to {0}.", name); } } return name; } @Override public String getMultiplicity(String propertyNameToCheck) { return null; } @Override public String getRelatedType(String propertyNameToCheck) { return null; } public String getPropertySource(final String propertyName, final boolean outgoing) { return getPropertySource(propertyName, outgoing, false); } public String getPropertySource(final String propertyName, final boolean outgoing, final boolean newStatementOnly) { final StringBuilder buf = new StringBuilder(); final String _sourceMultiplicity = getProperty(sourceMultiplicity); final String _targetMultiplicity = getProperty(targetMultiplicity); final String _sourceNotion = getProperty(sourceNotion); final String _targetNotion = getProperty(targetNotion); final String _sourceType = getSchemaNodeSourceType(); final String _targetType = getSchemaNodeTargetType(); final String _className = getClassName(); if (outgoing) { if ("1".equals(_targetMultiplicity)) { if (!newStatementOnly) { buf.append("\tpublic static final Property<").append(_targetType).append("> ") .append(SchemaHelper.cleanPropertyName(propertyName)).append("Property"); buf.append(" = "); } buf.append("new EndNode<>(\"").append(propertyName).append("\", ").append(_className) .append(".class"); buf.append(getNotion(_sourceType, _targetNotion)); buf.append(newStatementOnly ? ")" : ").dynamic();\n"); } else { if (!newStatementOnly) { buf.append("\tpublic static final Property<java.util.List<").append(_targetType).append(">> ") .append(SchemaHelper.cleanPropertyName(propertyName)).append("Property"); buf.append(" = "); } buf.append("new EndNodes<>(\"").append(propertyName).append("\", ").append(_className) .append(".class"); buf.append(getNotion(_sourceType, _targetNotion)); buf.append(newStatementOnly ? ")" : ").dynamic();\n"); } } else { if ("1".equals(_sourceMultiplicity)) { if (!newStatementOnly) { buf.append("\tpublic static final Property<").append(_sourceType).append("> ") .append(SchemaHelper.cleanPropertyName(propertyName)).append("Property"); buf.append(" = "); } buf.append("new StartNode<>(\"").append(propertyName).append("\", ").append(_className) .append(".class"); buf.append(getNotion(_targetType, _sourceNotion)); buf.append(newStatementOnly ? ")" : ").dynamic();\n"); } else { if (!newStatementOnly) { buf.append("\tpublic static final Property<java.util.List<").append(_sourceType).append(">> ") .append(SchemaHelper.cleanPropertyName(propertyName)).append("Property"); buf.append(" = "); } buf.append("new StartNodes<>(\"").append(propertyName).append("\", ").append(_className) .append(".class"); buf.append(getNotion(_targetType, _sourceNotion)); buf.append(newStatementOnly ? ")" : ").dynamic();\n"); } } return buf.toString(); } public String getMultiplicity(final boolean outgoing) { if (outgoing) { return getProperty(targetMultiplicity); } else { return getProperty(sourceMultiplicity); } } public String getPropertyName(final String relatedClassName, final Set<String> existingPropertyNames, final boolean outgoing) { final String relationshipTypeName = getProperty(SchemaRelationshipNode.relationshipType).toLowerCase(); final String _sourceType = getSchemaNodeSourceType(); final String _targetType = getSchemaNodeTargetType(); final String _targetJsonName = getProperty(targetJsonName); final String _targetMultiplicity = getProperty(targetMultiplicity); final String _sourceJsonName = getProperty(sourceJsonName); final String _sourceMultiplicity = getProperty(sourceMultiplicity); final String propertyName = SchemaRelationshipNode.getPropertyName(relatedClassName, existingPropertyNames, outgoing, relationshipTypeName, _sourceType, _targetType, _targetJsonName, _targetMultiplicity, _sourceJsonName, _sourceMultiplicity); try { if (outgoing) { if (_targetJsonName == null) { setProperty(previousTargetJsonName, propertyName); } } else { if (_sourceJsonName == null) { setProperty(previousSourceJsonName, propertyName); } } } catch (FrameworkException fex) { fex.printStackTrace(); } return propertyName; } public static String getPropertyName(final String relatedClassName, final Set<String> existingPropertyNames, final boolean outgoing, final String relationshipTypeName, final String _sourceType, final String _targetType, final String _targetJsonName, final String _targetMultiplicity, final String _sourceJsonName, final String _sourceMultiplicity) { String propertyName = ""; if (outgoing) { if (_targetJsonName != null) { // FIXME: no automatic creation? propertyName = _targetJsonName; } else { if ("1".equals(_targetMultiplicity)) { propertyName = CaseHelper.toLowerCamelCase(relationshipTypeName) + CaseHelper.toUpperCamelCase(_targetType); } else { propertyName = CaseHelper.plural(CaseHelper.toLowerCamelCase(relationshipTypeName) + CaseHelper.toUpperCamelCase(_targetType)); } } } else { if (_sourceJsonName != null) { propertyName = _sourceJsonName; } else { if ("1".equals(_sourceMultiplicity)) { propertyName = CaseHelper.toLowerCamelCase(_sourceType) + CaseHelper.toUpperCamelCase(relationshipTypeName); } else { propertyName = CaseHelper.plural(CaseHelper.toLowerCamelCase(_sourceType) + CaseHelper.toUpperCamelCase(relationshipTypeName)); } } } if (existingPropertyNames.contains(propertyName)) { // First level: Add direction suffix propertyName += outgoing ? "Out" : "In"; int i = 0; // New name still exists: Add number while (existingPropertyNames.contains(propertyName)) { propertyName += ++i; } } existingPropertyNames.add(propertyName); return propertyName; } @Override public String getSource(final ErrorBuffer errorBuffer) throws FrameworkException { final Map<Actions.Type, List<ActionEntry>> actions = new LinkedHashMap<>(); final Map<String, Set<String>> viewProperties = new LinkedHashMap<>(); final StringBuilder src = new StringBuilder(); final Class baseType = AbstractRelationship.class; final String _className = getClassName(); final String _sourceNodeType = getSchemaNodeSourceType(); final String _targetNodeType = getSchemaNodeTargetType(); final Set<String> propertyNames = new LinkedHashSet<>(); final Set<Validator> validators = new LinkedHashSet<>(); final Set<String> enums = new LinkedHashSet<>(); final Set<String> interfaces = new LinkedHashSet<>(); src.append("package org.structr.dynamic;\n\n"); SchemaHelper.formatImportStatements(src, baseType); src.append("public class ").append(_className).append(" extends ").append(getBaseType()); if ("OWNS".equals(getProperty(relationshipType))) { interfaces.add(Ownership.class.getName()); } if (!Direction.None.equals(getProperty(permissionPropagation))) { interfaces.add(PermissionPropagation.class.getName()); } // append interfaces if present if (!interfaces.isEmpty()) { src.append(" implements "); for (final Iterator<String> it = interfaces.iterator(); it.hasNext();) { src.append(it.next()); if (it.hasNext()) { src.append(", "); } } } src.append(" {\n\n"); if (!Direction.None.equals(getProperty(permissionPropagation))) { src.append("\tstatic {\n\t\tSchemaRelationshipNode.registerPropagatingRelationshipType(\"") .append(getRelationshipType()).append("\");\n\t}\n\n"); } src.append(SchemaHelper.extractProperties(this, propertyNames, validators, enums, viewProperties, errorBuffer)); src.append(SchemaHelper.extractViews(this, viewProperties, errorBuffer)); src.append(SchemaHelper.extractMethods(this, actions)); // source and target id properties src.append( "\tpublic static final Property<java.lang.String> sourceIdProperty = new SourceId(\"sourceId\");\n"); src.append( "\tpublic static final Property<java.lang.String> targetIdProperty = new TargetId(\"targetId\");\n"); // add sourceId and targetId to view properties //SchemaHelper.addPropertyToView(PropertyView.Public, "sourceId", viewProperties); //SchemaHelper.addPropertyToView(PropertyView.Public, "targetId", viewProperties); SchemaHelper.addPropertyToView(PropertyView.Ui, "sourceId", viewProperties); SchemaHelper.addPropertyToView(PropertyView.Ui, "targetId", viewProperties); // output possible enum definitions for (final String enumDefition : enums) { src.append(enumDefition); } for (Map.Entry<String, Set<String>> entry : viewProperties.entrySet()) { final String viewName = entry.getKey(); final Set<String> view = entry.getValue(); if (!view.isEmpty()) { dynamicViews.add(viewName); SchemaHelper.formatView(src, _className, viewName, viewName, view); } } // abstract method implementations src.append("\n\t@Override\n"); src.append("\tpublic Class<").append(_sourceNodeType).append("> getSourceType() {\n"); src.append("\t\treturn ").append(_sourceNodeType).append(".class;\n"); src.append("\t}\n\n"); src.append("\t@Override\n"); src.append("\tpublic Class<").append(_targetNodeType).append("> getTargetType() {\n"); src.append("\t\treturn ").append(_targetNodeType).append(".class;\n"); src.append("\t}\n\n"); src.append("\t@Override\n"); src.append("\tpublic Property<java.lang.String> getSourceIdProperty() {\n"); src.append("\t\treturn sourceId;\n"); src.append("\t}\n\n"); src.append("\t@Override\n"); src.append("\tpublic Property<java.lang.String> getTargetIdProperty() {\n"); src.append("\t\treturn targetId;\n"); src.append("\t}\n\n"); src.append("\t@Override\n"); src.append("\tpublic java.lang.String name() {\n"); src.append("\t\treturn \"").append(getRelationshipType()).append("\";\n"); src.append("\t}\n\n"); SchemaHelper.formatValidators(src, validators); SchemaHelper.formatSaveActions(src, actions); formatRelationshipFlags(src); formatPermissionPropagation(src); src.append("}\n"); return src.toString(); } @Override public Set<String> getViews() { return dynamicViews; } @Override public String getAuxiliarySource() throws FrameworkException { final Set<String> existingPropertyNames = new LinkedHashSet<>(); final String sourceNodeType = getSchemaNodeSourceType(); final String targetNodeType = getSchemaNodeTargetType(); final StringBuilder src = new StringBuilder(); final String _className = getClassName(); final Class baseType = AbstractRelationship.class; if (!"File".equals(sourceNodeType) && !"File".equals(targetNodeType)) { return null; } src.append("package org.structr.dynamic;\n\n"); SchemaHelper.formatImportStatements(src, baseType); src.append("public class _").append(_className).append("Helper {\n\n"); src.append("\n\tstatic {\n\n"); src.append("\t\tfinal PropertyKey outKey = "); src.append(getPropertySource(getPropertyName(sourceNodeType, existingPropertyNames, true), true, true)); src.append(";\n"); src.append("\t\toutKey.setDeclaringClass(").append(sourceNodeType).append(".class);\n\n"); src.append("\t\tfinal PropertyKey inKey = "); src.append(getPropertySource(getPropertyName(targetNodeType, existingPropertyNames, false), false, true)); src.append(";\n"); src.append("\t\tinKey.setDeclaringClass(").append(targetNodeType).append(".class);\n\n"); src.append("\t\tStructrApp.getConfiguration().registerDynamicProperty("); src.append(sourceNodeType).append(".class, outKey);\n"); src.append("\t\tStructrApp.getConfiguration().registerDynamicProperty("); src.append(targetNodeType).append(".class, inKey);\n\n"); src.append("\t\tStructrApp.getConfiguration().registerPropertySet(").append(sourceNodeType) .append(".class, PropertyView.Ui, outKey);\n"); src.append("\t\tStructrApp.getConfiguration().registerPropertySet(").append(targetNodeType) .append(".class, PropertyView.Ui, inKey);\n"); src.append("\t}\n"); src.append("}\n"); return src.toString(); // return null; } // ----- public methods ----- public String getSchemaNodeSourceType() { final SchemaNode sourceNode = getSourceNode(); if (sourceNode != null) { return sourceNode.getProperty(SchemaNode.name); } return null; } public String getSchemaNodeTargetType() { final SchemaNode targetNode = getTargetNode(); if (targetNode != null) { return targetNode.getProperty(SchemaNode.name); } return null; } @Override public String getResourceSignature() { final String _sourceType = getSchemaNodeSourceType(); final String _targetType = getSchemaNodeTargetType(); return _sourceType + "/" + _targetType; } public String getInverseResourceSignature() { final String _sourceType = getSchemaNodeSourceType(); final String _targetType = getSchemaNodeTargetType(); return _targetType + "/" + _sourceType; } // ----- private methods ----- private String getRelationshipType() { String relType = getProperty(relationshipType); if (relType == null) { final String _sourceType = getSchemaNodeSourceType().toUpperCase(); final String _targetType = getSchemaNodeTargetType().toUpperCase(); relType = _sourceType + "_" + _targetType; } return relType; } private String getNotion(final String _className, final String notionSource) { final StringBuilder buf = new StringBuilder(); if (StringUtils.isNotBlank(notionSource)) { final Set<String> keys = new LinkedHashSet<>(Arrays.asList(notionSource.split("[\\s,]+"))); if (!keys.isEmpty()) { if (keys.size() == 1) { String key = keys.iterator().next(); boolean create = key.startsWith("+"); if (create) { key = key.substring(1); } if (ValidKeyPattern.matcher(key).matches()) { buf.append(", new PropertyNotion("); buf.append(getNotionKey(_className, key)); buf.append(", ").append(create); buf.append(")"); } else { logger.log(Level.WARNING, "Invalid key name {0} for notion.", key); } } else { buf.append(", new PropertySetNotion("); // use only matching keys for (final Iterator<String> it = Iterables.filter(new KeyMatcher(), keys).iterator(); it .hasNext();) { buf.append(getNotionKey(_className, it.next())); if (it.hasNext()) { buf.append(", "); } } buf.append(")"); } } } return buf.toString(); } private String getNotionKey(final String _className, final String key) { return _className + "." + key; } private String getBaseType() { final String _sourceMultiplicity = getProperty(sourceMultiplicity); final String _targetMultiplicity = getProperty(targetMultiplicity); final String _sourceType = getSchemaNodeSourceType(); final String _targetType = getSchemaNodeTargetType(); final StringBuilder buf = new StringBuilder(); if ("1".equals(_sourceMultiplicity)) { if ("1".equals(_targetMultiplicity)) { buf.append("OneToOne"); } else { buf.append("OneToMany"); } } else { if ("1".equals(_targetMultiplicity)) { buf.append("ManyToOne"); } else { buf.append("ManyToMany"); } } buf.append("<"); buf.append(_sourceType); buf.append(", "); buf.append(_targetType); buf.append(">"); return buf.toString(); } public void resolveCascadingEnums(final Cascade delete, final Cascade autoCreate) throws FrameworkException { if (delete != null) { switch (delete) { case sourceToTarget: setProperty(SchemaRelationshipNode.cascadingDeleteFlag, Long.valueOf(Relation.SOURCE_TO_TARGET)); break; case targetToSource: setProperty(SchemaRelationshipNode.cascadingDeleteFlag, Long.valueOf(Relation.TARGET_TO_SOURCE)); break; case always: setProperty(SchemaRelationshipNode.cascadingDeleteFlag, Long.valueOf(Relation.ALWAYS)); break; case constraintBased: setProperty(SchemaRelationshipNode.cascadingDeleteFlag, Long.valueOf(Relation.CONSTRAINT_BASED)); break; } } if (autoCreate != null) { switch (autoCreate) { case sourceToTarget: setProperty(SchemaRelationshipNode.autocreationFlag, Long.valueOf(Relation.SOURCE_TO_TARGET)); break; case targetToSource: setProperty(SchemaRelationshipNode.autocreationFlag, Long.valueOf(Relation.TARGET_TO_SOURCE)); break; case always: setProperty(SchemaRelationshipNode.autocreationFlag, Long.valueOf(Relation.ALWAYS)); break; case constraintBased: setProperty(SchemaRelationshipNode.autocreationFlag, Long.valueOf(Relation.CONSTRAINT_BASED)); break; } } } public Map<String, Object> resolveCascadingFlags() { final Long cascadingDelete = getProperty(SchemaRelationshipNode.cascadingDeleteFlag); final Long autoCreate = getProperty(SchemaRelationshipNode.autocreationFlag); final Map<String, Object> cascade = new TreeMap<>(); if (cascadingDelete != null) { switch (cascadingDelete.intValue()) { case Relation.SOURCE_TO_TARGET: cascade.put(JsonSchema.KEY_DELETE, JsonSchema.Cascade.sourceToTarget.name()); break; case Relation.TARGET_TO_SOURCE: cascade.put(JsonSchema.KEY_DELETE, JsonSchema.Cascade.targetToSource.name()); break; case Relation.ALWAYS: cascade.put(JsonSchema.KEY_DELETE, JsonSchema.Cascade.always.name()); break; case Relation.CONSTRAINT_BASED: cascade.put(JsonSchema.KEY_DELETE, JsonSchema.Cascade.constraintBased.name()); break; } } if (autoCreate != null) { switch (autoCreate.intValue()) { case Relation.SOURCE_TO_TARGET: cascade.put(JsonSchema.KEY_CREATE, JsonSchema.Cascade.sourceToTarget.name()); break; case Relation.TARGET_TO_SOURCE: cascade.put(JsonSchema.KEY_CREATE, JsonSchema.Cascade.targetToSource.name()); break; case Relation.ALWAYS: cascade.put(JsonSchema.KEY_CREATE, JsonSchema.Cascade.always.name()); break; case Relation.CONSTRAINT_BASED: cascade.put(JsonSchema.KEY_CREATE, JsonSchema.Cascade.constraintBased.name()); break; } } return cascade; } // ----- interface Syncable ----- @Override public List<GraphObject> getSyncData() throws FrameworkException { final List<GraphObject> syncables = super.getSyncData(); syncables.add(getSourceNode()); syncables.add(getTargetNode()); return syncables; } // ----- private methods ----- private void formatRelationshipFlags(final StringBuilder src) { Long cascadingDelete = getProperty(cascadingDeleteFlag); if (cascadingDelete != null) { src.append("\n\t@Override\n"); src.append("\tpublic int getCascadingDeleteFlag() {\n"); switch (cascadingDelete.intValue()) { case Relation.ALWAYS: src.append("\t\treturn Relation.ALWAYS;\n"); break; case Relation.CONSTRAINT_BASED: src.append("\t\treturn Relation.CONSTRAINT_BASED;\n"); break; case Relation.SOURCE_TO_TARGET: src.append("\t\treturn Relation.SOURCE_TO_TARGET;\n"); break; case Relation.TARGET_TO_SOURCE: src.append("\t\treturn Relation.TARGET_TO_SOURCE;\n"); break; case Relation.NONE: default: src.append("\t\treturn Relation.NONE;\n"); } src.append("\t}\n\n"); } Long autocreate = getProperty(autocreationFlag); if (autocreate != null) { src.append("\n\t@Override\n"); src.append("\tpublic int getAutocreationFlag() {\n"); switch (autocreate.intValue()) { case Relation.ALWAYS: src.append("\t\treturn Relation.ALWAYS;\n"); break; case Relation.SOURCE_TO_TARGET: src.append("\t\treturn Relation.SOURCE_TO_TARGET;\n"); break; case Relation.TARGET_TO_SOURCE: src.append("\t\treturn Relation.TARGET_TO_SOURCE;\n"); break; default: src.append("\t\treturn Relation.NONE;\n"); } src.append("\t}\n\n"); } } private void formatPermissionPropagation(final StringBuilder buf) { if (!Direction.None.equals(getProperty(permissionPropagation))) { buf.append("\n\t@Override\n"); buf.append("\tpublic SchemaRelationshipNode.Direction getPropagationDirection() {\n"); buf.append("\t\treturn SchemaRelationshipNode.Direction.").append(getProperty(permissionPropagation)) .append(";\n"); buf.append("\t}\n\n"); buf.append("\n\t@Override\n"); buf.append("\tpublic SchemaRelationshipNode.Propagation getReadPropagation() {\n"); buf.append("\t\treturn SchemaRelationshipNode.Propagation.").append(getProperty(readPropagation)) .append(";\n"); buf.append("\t}\n\n"); buf.append("\n\t@Override\n"); buf.append("\tpublic SchemaRelationshipNode.Propagation getWritePropagation() {\n"); buf.append("\t\treturn SchemaRelationshipNode.Propagation.").append(getProperty(writePropagation)) .append(";\n"); buf.append("\t}\n\n"); buf.append("\n\t@Override\n"); buf.append("\tpublic SchemaRelationshipNode.Propagation getDeletePropagation() {\n"); buf.append("\t\treturn SchemaRelationshipNode.Propagation.").append(getProperty(deletePropagation)) .append(";\n"); buf.append("\t}\n\n"); buf.append("\n\t@Override\n"); buf.append("\tpublic SchemaRelationshipNode.Propagation getAccessControlPropagation() {\n"); buf.append("\t\treturn SchemaRelationshipNode.Propagation.") .append(getProperty(accessControlPropagation)).append(";\n"); buf.append("\t}\n\n"); buf.append("\n\t@Override\n"); buf.append("\tpublic String getDeltaProperties() {\n"); final String _propertyMask = getProperty(propertyMask); if (_propertyMask != null) { buf.append("\t\treturn \"").append(_propertyMask).append("\";\n"); } else { buf.append("\t\treturn null;\n"); } buf.append("\t}\n\n"); } } private void checkClassName() throws FrameworkException { final String className = getClassName(); final String potentialNewClassName = assembleNewClassName(); if (!className.equals(potentialNewClassName)) { try { setProperty(AbstractNode.name, potentialNewClassName); } catch (FrameworkException fex) { logger.log(Level.WARNING, "Unable to set relationship name to {0}.", potentialNewClassName); } } } private String assembleNewClassName() { final String _sourceType = getSchemaNodeSourceType(); final String _targetType = getSchemaNodeTargetType(); final String _relType = SchemaHelper.cleanPropertyName(getRelationshipType()); return _sourceType + _relType + _targetType; } private void checkAndRenameSourceAndTargetJsonNames() throws FrameworkException { final String _previousSourceJsonName = getProperty(previousSourceJsonName); final String _previousTargetJsonName = getProperty(previousTargetJsonName); final String _currentSourceJsonName = ((getProperty(sourceJsonName) != null) ? getProperty(sourceJsonName) : getPropertyName(getSchemaNodeTargetType(), new LinkedHashSet<>(), false)); final String _currentTargetJsonName = ((getProperty(targetJsonName) != null) ? getProperty(targetJsonName) : getPropertyName(getSchemaNodeSourceType(), new LinkedHashSet<>(), true)); final SchemaNode _sourceNode = getProperty(sourceNode); final SchemaNode _targetNode = getProperty(targetNode); if (_previousSourceJsonName != null && _currentSourceJsonName != null && !_currentSourceJsonName.equals(_previousSourceJsonName)) { renameNameInNonGraphProperties(_targetNode, _previousSourceJsonName, _currentSourceJsonName); renameNotionPropertyReferences(_sourceNode, _previousSourceJsonName, _currentSourceJsonName); renameNotionPropertyReferences(_targetNode, _previousSourceJsonName, _currentSourceJsonName); } if (_previousTargetJsonName != null && _currentTargetJsonName != null && !_currentTargetJsonName.equals(_previousTargetJsonName)) { renameNameInNonGraphProperties(_sourceNode, _previousTargetJsonName, _currentTargetJsonName); renameNotionPropertyReferences(_sourceNode, _previousTargetJsonName, _currentTargetJsonName); renameNotionPropertyReferences(_targetNode, _previousTargetJsonName, _currentTargetJsonName); } } private void removeSourceAndTargetJsonNames(PropertyMap properties) throws FrameworkException { final SchemaNode _sourceNode = getProperty(sourceNode); final SchemaNode _targetNode = getProperty(targetNode); final String _currentSourceJsonName = (properties.get(sourceJsonName) != null) ? properties.get(sourceJsonName) : properties.get(previousSourceJsonName); final String _currentTargetJsonName = (properties.get(targetJsonName) != null) ? properties.get(targetJsonName) : properties.get(previousTargetJsonName); if (_sourceNode != null) { removeNameFromNonGraphProperties(_sourceNode, _currentSourceJsonName); removeNameFromNonGraphProperties(_sourceNode, _currentTargetJsonName); } if (_targetNode != null) { removeNameFromNonGraphProperties(_targetNode, _currentSourceJsonName); removeNameFromNonGraphProperties(_targetNode, _currentTargetJsonName); } } private void renameNotionPropertyReferences(final SchemaNode schemaNode, final String previousValue, final String currentValue) throws FrameworkException { // examine properties of other node for (final SchemaProperty property : schemaNode.getSchemaProperties()) { if (Type.Notion.equals(property.getPropertyType())) { // try to rename final String basePropertyName = property.getNotionBaseProperty(); if (basePropertyName.equals(previousValue)) { property.setProperty(SchemaProperty.format, property.getFormat().replace(previousValue, currentValue)); } } } } private void renameNameInNonGraphProperties(final AbstractSchemaNode schemaNode, final String toRemove, final String newValue) throws FrameworkException { // examine all views for (final SchemaView view : schemaNode.getSchemaViews()) { final String nonGraphProperties = view.getProperty(SchemaView.nonGraphProperties); if (nonGraphProperties != null) { final ArrayList<String> properties = new ArrayList<>( Arrays.asList(nonGraphProperties.split("[, ]+"))); final int pos = properties.indexOf(toRemove); if (pos != -1) { properties.set(pos, newValue); } view.setProperty(SchemaView.nonGraphProperties, StringUtils.join(properties, ", ")); } } } private void removeNameFromNonGraphProperties(final AbstractSchemaNode schemaNode, final String toRemove) throws FrameworkException { // examine all views for (final SchemaView view : schemaNode.getSchemaViews()) { final String nonGraphProperties = view.getProperty(SchemaView.nonGraphProperties); if (nonGraphProperties != null) { final ArrayList<String> properties = new ArrayList<>( Arrays.asList(nonGraphProperties.split("[, ]+"))); properties.remove(toRemove); view.setProperty(SchemaView.nonGraphProperties, StringUtils.join(properties, ", ")); } } } // ----- public static methods ----- public static String getDefaultRelationshipType(final SchemaRelationshipNode rel) { return getDefaultRelationshipType(rel.getSourceNode(), rel.getTargetNode()); } public static String getDefaultRelationshipType(final SchemaNode sourceNode, final SchemaNode targetNode) { return getDefaultRelationshipType(sourceNode.getName(), targetNode.getName()); } public static String getDefaultRelationshipType(final String sourceType, final String targetType) { return sourceType + "_" + targetType; } // ----- nested classes ----- private static class KeyMatcher implements Predicate<String> { @Override public boolean accept(String t) { if (ValidKeyPattern.matcher(t).matches()) { return true; } logger.log(Level.WARNING, "Invalid key name {0} for notion.", t); return false; } } }