Java tutorial
/*************************************************************** * This file is part of the [fleXive](R) backend application. * * Copyright (c) 1999-2014 * UCS - unique computing solutions gmbh (http://www.ucs.at) * All rights reserved * * The [fleXive](R) backend application 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 2 of the License, or (at your option) any * later version. * * The GNU General Public License can be found at * http://www.gnu.org/licenses/gpl.html. * A copy is found in the textfile GPL.txt and important notices to the * license from the author are found in LICENSE.txt distributed with * these libraries. * * This library 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. * * For further information about UCS - unique computing solutions gmbh, * please see the company website: http://www.ucs.at * * For further information about [fleXive](R), please see the * project website: http://www.flexive.org * * * This copyright notice MUST APPEAR in all copies of the file! ***************************************************************/ package com.flexive.war.javascript.tree; import com.flexive.shared.CacheAdmin; import com.flexive.shared.EJBLookup; import com.flexive.shared.XPathElement; import com.flexive.shared.exceptions.FxApplicationException; import com.flexive.shared.exceptions.FxInvalidParameterException; import com.flexive.shared.exceptions.FxNotFoundException; import com.flexive.shared.structure.*; import com.flexive.shared.value.FxString; import org.apache.commons.lang.StringUtils; import java.io.Serializable; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Content tree edit actions invoked via JSON/RPC. * * @author Gerhard Glos (gerhard.glos@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at) * @version $Rev$ */ public class StructureTreeEditor implements Serializable { private static final long serialVersionUID = -2853036616736591794L; private static Pattern aliasPattern = Pattern.compile("[a-zA-Z][a-zA-Z_0-9]*"); /** * Remove an assignment, breaking inheritance if needed * * @param id the assignment id to remove * @throws FxApplicationException on errors */ public void deleteAssignment(long id) throws FxApplicationException { EJBLookup.getAssignmentEngine().removeAssignment(id); } /** * Remove a type * * @param id id of the type to remove * @throws FxApplicationException on errors */ public void deleteType(long id) throws FxApplicationException { EJBLookup.getTypeEngine().remove(id); } /** * Reuse a property assignment * * @param orgAssignmentId id of the assignment to reuse * @param newName new name (can be empty, will be used for label if set) * @param xPath XPath * @param type FxType * @return FxPropertyAssignmentEdit * @throws FxNotFoundException on errors * @throws FxInvalidParameterException on errors */ private FxPropertyAssignmentEdit createReusedPropertyAssignment(long orgAssignmentId, String newName, String xPath, FxType type) throws FxNotFoundException, FxInvalidParameterException { FxPropertyAssignment assignment = (FxPropertyAssignment) CacheAdmin.getEnvironment() .getAssignment(orgAssignmentId); FxPropertyAssignmentEdit prop; if (!StringUtils.isEmpty(newName)) { prop = FxPropertyAssignmentEdit.createNew(assignment, type, newName == null ? assignment.getAlias() : newName, xPath); prop.getLabel().setDefaultTranslation(StringUtils.capitalize(newName)); } else prop = FxPropertyAssignmentEdit.createNew(assignment, type, assignment.getAlias(), xPath); return prop; } /** * Creates a new group assignment from a given group assignment * with optionally a new alias which is also used as label if specified. * * @param orgAssignmentId source assignment * @param newAlias new alias (may be null) * @param xPath destination xpath * @param type destination type * @return the created group assignment * @throws FxNotFoundException * @throws FxInvalidParameterException */ private FxGroupAssignmentEdit createReusedGroupAssignment(long orgAssignmentId, String newAlias, String xPath, FxType type) throws FxNotFoundException, FxInvalidParameterException { FxGroupAssignment assignment = (FxGroupAssignment) CacheAdmin.getEnvironment() .getAssignment(orgAssignmentId); FxGroupAssignmentEdit group; group = FxGroupAssignmentEdit.createNew(assignment, type, StringUtils.isBlank(newAlias) ? assignment.getAlias() : newAlias, xPath); if (!StringUtils.isBlank(newAlias)) group.getLabel().setDefaultTranslation(StringUtils.capitalize(newAlias)); return group; } /** * Creates a derived assignment from a given assignment and pastes it into the * the target group or type at the first position. A new alias can also be specified. * * @param parentAssId the id from which the assignment will be derived * @param parentNodeType the nodeType from which the assignment will be derived (i.e. StructureTreeWriter.NODE_TYPE_GROUP, StructureTreeWriter.NODE_TYPE_ASSIGNMENT) * @param targetId the id of the group or type the assignment will be pasted into. * @param targetNodeType the node type of the target (i.e. StructureTreeWriter.NODE_TYPE_GROUP, StructureTreeWriter.NODE_TYPE_TYPE). * @param newName the new alias. if ==null the old will be taken. * @return the id of the newly created assignment * @throws com.flexive.shared.exceptions.FxApplicationException * on errors */ public long pasteAssignmentInto(long parentAssId, String parentNodeType, long targetId, String targetNodeType, String newName) throws FxApplicationException { String targetXPath = "/"; FxType targetType = null; long assignmentId = -1; if (StructureTreeWriter.NODE_TYPE_GROUP.equals(targetNodeType)) { FxGroupAssignment ga = (FxGroupAssignment) CacheAdmin.getEnvironment().getAssignment(targetId); targetType = ga.getAssignedType(); targetXPath = ga.getXPath(); } else if (StructureTreeWriter.NODE_TYPE_TYPE.equals(targetNodeType) || StructureTreeWriter.NODE_TYPE_TYPE_RELATION.equals(targetNodeType)) { targetType = CacheAdmin.getEnvironment().getType(targetId); } //paste assignment into the target group/type if (StructureTreeWriter.NODE_TYPE_ASSIGNMENT.equals(parentNodeType)) { assignmentId = EJBLookup.getAssignmentEngine().save( createReusedPropertyAssignment(parentAssId, newName, targetXPath, targetType).setPosition(0), false); //move property assignment to first position //FxPropertyAssignmentEdit pa = ((FxPropertyAssignment) CacheAdmin.getEnvironment().getAssignment(assignmentId)).asEditable(); //pa.setPosition(0); //EJBLookup.getAssignmentEngine().save(pa, false); } else if (StructureTreeWriter.NODE_TYPE_GROUP.equals(parentNodeType)) { FxGroupAssignment assignment = (FxGroupAssignment) CacheAdmin.getEnvironment() .getAssignment(parentAssId); assignmentId = EJBLookup.getAssignmentEngine() .save(createReusedGroupAssignment(assignment.getId(), newName, targetXPath, targetType) .setPosition(0), true); //assignmentId = EJBLookup.getAssignmentEngine().save(FxGroupAssignmentEdit.createNew(assignment, targetType, newName == null ? assignment.getAlias() : newName, targetXPath).setPosition(0), true); //move group assignment to first position //FxGroupAssignmentEdit ga = ((FxGroupAssignment) CacheAdmin.getEnvironment().getAssignment(assignmentId)).asEditable(); //ga.setPosition(0); //EJBLookup.getAssignmentEngine().save(ga, false); } return assignmentId; } /** * Creates a derived assignment from a given assignment and pastes it at * a relative position above or below (indicated by steps) a destination assignment. * A new alias can also be specified. * * @param srcId the id from which the assignment will be derived * @param srcNodeType the nodeType from which the assignment will be derived (i.e. StructureTreeWriter.NODE_TYPE_GROUP, StructureTreeWriter.NODE_TYPE_ASSIGNMENT) * @param destId the id of the destination assignment, where the assignment will be pasted at a relative position * @param destNodeType the node type of the destination assignment * @param newName the new alias. if ==null the old will be taken. * @param steps the position relative to the destination assignment, where the derived assignment will be pasted. * @return the id of the newly created assignment * @throws com.flexive.shared.exceptions.FxApplicationException * on errors */ public long pasteAssignmentRelative(long srcId, String srcNodeType, long destId, String destNodeType, String newName, int steps) throws FxApplicationException { long assignmentId = -1; FxAssignment destAssignment = CacheAdmin.getEnvironment().getAssignment(destId); FxType destType = destAssignment.getAssignedType(); String destXPath = XPathElement.stripLastElement(destAssignment.getXPath()); // for types it seems that Xpath must end with a "/" TODO: resolve xpath issue. if (destXPath.equals(destType.getName())) destXPath = destXPath + "/"; // create new assignment if (StructureTreeWriter.NODE_TYPE_GROUP.equals(srcNodeType)) { FxGroupAssignment srcAssignment = (FxGroupAssignment) CacheAdmin.getEnvironment().getAssignment(srcId); //create assignment FxGroupAssignmentEdit newAssignment = createReusedGroupAssignment(srcAssignment.getId(), newName, destXPath, destType); //set position newAssignment.setPosition(destAssignment.getPosition() + steps); //save newly created assignment to db assignmentId = EJBLookup.getAssignmentEngine().save(newAssignment, true); } else if (StructureTreeWriter.NODE_TYPE_ASSIGNMENT.equals(srcNodeType)) { //create assignment FxPropertyAssignmentEdit newAssignment = createReusedPropertyAssignment(srcId, newName, destXPath, destType); //set position newAssignment.setPosition(destAssignment.getPosition() + steps); //save newly created assignment to db assignmentId = EJBLookup.getAssignmentEngine().save(newAssignment, false); } else { throw new FxInvalidParameterException("nodeType", "ex.structureTreeEditor.nodeType.invalid", srcNodeType); } return assignmentId; } /** * Moves a source assignment to a relative position of another destination assignment. * The assignments need to be at the same hierarchy level for positioning to work properly. * Steps indicates the relative position offset: * If steps is -1,-2..n the source assignment will be moved 1,2..n positions before the destination assignment. * If steps is 1,2..n the source assignment will be moved 1,2..n positions after the destination assignment. * * @param srcId the id of the assignment that shall be moved. * @param srcNodeType the node type of the assignment to be moved. * @param destId the id of the destination assignment relative to which the source assignment will be moved. * @param steps relative position offset. * @throws com.flexive.shared.exceptions.FxApplicationException * if the node type doesn't match StructureTreeWriter.NODE_TYPE_GROUP or StructureTreeWriter.NODE_TYPE_ASSIGNMENT */ public void moveAssignmentRelative(long srcId, String srcNodeType, long destId, int steps) throws FxApplicationException { FxAssignment dest = CacheAdmin.getEnvironment().getAssignment(destId); if (StructureTreeWriter.NODE_TYPE_GROUP.equals(srcNodeType)) { FxGroupAssignmentEdit src = ((FxGroupAssignment) CacheAdmin.getEnvironment().getAssignment(srcId)) .asEditable(); //if the source position is smaller than the destination position, an offset of -1 needs to be added if (src.getPosition() < dest.getPosition()) steps = steps - 1; src.setPosition(dest.getPosition() + steps); EJBLookup.getAssignmentEngine().save(src, false); } else if (StructureTreeWriter.NODE_TYPE_ASSIGNMENT.equals(srcNodeType)) { FxPropertyAssignmentEdit src = ((FxPropertyAssignment) CacheAdmin.getEnvironment().getAssignment(srcId)) .asEditable(); //if the source position is smaller than the destination position, an offset of -1 needs to be added if (src.getPosition() < dest.getPosition()) steps = steps - 1; src.setPosition(dest.getPosition() + steps); EJBLookup.getAssignmentEngine().save(src, false); } else throw new FxInvalidParameterException("nodeType", "ex.structureTreeEditor.nodeType.invalid", srcNodeType); } public boolean validateAlias(String alias) { if (alias != null) { Matcher m = aliasPattern.matcher(alias); if (m.matches()) return true; //all correct } return false; } /** * Compares if two assignments are positioned at the same hierarchy level. * * @param id1 id of first assignment * @param id2 id of second assignment * @return true if they have the same parent type, or if parent group assignments exist, true if they have the same parent group assignment */ public boolean isSameLevel(long id1, long id2) { FxAssignment a1 = CacheAdmin.getEnvironment().getAssignment(id1); FxAssignment a2 = CacheAdmin.getEnvironment().getAssignment(id2); if (a1.hasParentGroupAssignment() && a2.hasParentGroupAssignment() && a1.getParentGroupAssignment().getId() == a2.getParentGroupAssignment().getId()) return true; else if (!a1.hasParentGroupAssignment() && !a2.hasParentGroupAssignment() && a1.getAssignedType().getId() == a2.getAssignedType().getId()) return true; return false; } /** * Checks if an assignment is the direct child of a given type or group. * * @param assId id of the assignment * @param parentId id of type or group assignment * @param parentNodeType the nodeDocType (i.e. StructureTreeWriter.NODE_TYPE_GROUP, StructureTreeWriter.NODE_TYPE_TYPE) of the parent * @return true if the assignment is a direct child of the type or group * @throws FxInvalidParameterException for invalid nodeDocTypes * @throws com.flexive.shared.exceptions.FxNotFoundException * on errors */ public boolean isDirectChild(long assId, long parentId, String parentNodeType) throws FxInvalidParameterException, FxNotFoundException { if (StructureTreeWriter.NODE_TYPE_GROUP.equals(parentNodeType)) { FxGroupAssignment ga = (FxGroupAssignment) CacheAdmin.getEnvironment().getAssignment(parentId); for (FxAssignment a : ga.getAssignments()) { if (a.getId() == assId) return true; } } else if (StructureTreeWriter.NODE_TYPE_TYPE.equals(parentNodeType) || StructureTreeWriter.NODE_TYPE_TYPE_RELATION.equals(parentNodeType)) { FxType type = CacheAdmin.getEnvironment().getType(parentId); for (FxAssignment a : type.getConnectedAssignments("/")) { if (a.getId() == assId) return true; } } else throw new FxInvalidParameterException("nodeType", "ex.structureTreeEditor.nodeType.invalid", parentNodeType); return false; } /** * Returns if an assignment with the specified id is the parent assignment of a child assignment with specified id. * * @param parent parent assignment id * @param child child assignment id * @return true the parent assignment is the parent of the child assignment. */ public boolean isParentAssignment(long parent, long child) { FxAssignment a = CacheAdmin.getEnvironment().getAssignment(child); while (a.hasParentGroupAssignment()) { if (a.getParentGroupAssignment().getId() == parent) return true; a = a.getParentGroupAssignment(); } return false; } /** * Returns if an FxPropertyAssignment (or its property repsectively) has set OPTION_SEARCHABLE to true * * @param assId assignment id * @return if an assignment is searchable */ public boolean isAssignmentSearchable(long assId) { return ((FxPropertyAssignment) CacheAdmin.getFilteredEnvironment().getAssignment(assId)).isSearchable(); } /** * Returns if a type still. (Used to check after deletion of a type if * the content page is still valid or if it displays a structure element * that does not exist anymore). * * @param id the id of the type * @return if the type with the specified id exists. */ public boolean isTypeExists(long id) { try { CacheAdmin.getFilteredEnvironment().getType(id); } catch (Exception e) { return false; } return true; } /** * Returns if an assignment exits. (Used to check after deletion if * the content page is still valid or or if it displays a structure element * that does not exist anymore). * * @param id the id of the assignment * @return if the type with the specified id exists. */ public boolean isAssignmentExists(long id) { try { CacheAdmin.getFilteredEnvironment().getAssignment(id); } catch (Exception e) { return false; } return true; } /** * Creates a derived type with the specified alias of the type with the specified parent * id and returns the id of the derived type. * * @param parentId the id of the parent type * @param alias the name of derived type * @return the id of the derived type */ public long createDerivedType(long parentId, String alias) throws FxApplicationException { FxType parent = CacheAdmin.getEnvironment().getType(parentId); FxTypeEdit derived = FxTypeEdit.createNew(alias, new FxString(parent.getLabel().isMultiLanguage(), alias), parent.getACL(), parent); derived.setIcon(parent.getIcon()); return EJBLookup.getTypeEngine().save(derived); } /** * Returns the display label for the given Type/Group/Assignment with the specified id. * * @param id the id * @param nodeType the nodeDocType (i.e. StructureTreeWriter.NODE_TYPE_GROUP, StructureTreeWriter.NODE_TYPE_TYPE,..) * @return the display label */ public String getLabel(long id, String nodeType) { if (StructureTreeWriter.NODE_TYPE_ASSIGNMENT.equals(nodeType) || StructureTreeWriter.NODE_TYPE_ASSIGNMENT_SYSTEMINTERNAL.equals(nodeType) || StructureTreeWriter.NODE_TYPE_GROUP.equals(nodeType)) { return CacheAdmin.getEnvironment().getAssignment(id).getDisplayName(); } else return CacheAdmin.getEnvironment().getType(id).getDisplayName(); } }