/******************************************************************************* * Copyright 2006 - 2012 Vienna University of Technology, * Department of Software Technology and Interactive Systems, IFS * * 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 eu.scape_project.planning.plato.wf; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import javax.ejb.Stateful; import javax.enterprise.context.ConversationScoped; import javax.inject.Inject; import org.slf4j.Logger; import eu.scape_project.planning.exception.PlanningException; import eu.scape_project.planning.manager.CriteriaManager; import eu.scape_project.planning.manager.StorageException; import eu.scape_project.planning.model.DigitalObject; import eu.scape_project.planning.model.PlanState; import eu.scape_project.planning.model.measurement.Measure; import eu.scape_project.planning.model.policy.ControlPolicy; import eu.scape_project.planning.model.policy.PreservationCase; import eu.scape_project.planning.model.tree.Leaf; import eu.scape_project.planning.model.tree.Node; import eu.scape_project.planning.model.tree.ObjectiveTree; import eu.scape_project.planning.model.tree.TreeNode; import eu.scape_project.planning.xml.ProjectExporter; import eu.scape_project.planning.xml.TreeLoader; /** * Business logic for workflow step Identify Requirements * * @author Markus Hamm, Michael Kraxner */ @Stateful @ConversationScoped public class IdentifyRequirements extends AbstractWorkflowStep { private static final long serialVersionUID = 1014116842177860229L; @Inject private Logger log; @Inject private TreeLoader treeLoader; @Inject private ProjectExporter projectExporter; @Inject private CriteriaManager criteriaManager; /** * Nodes to delete after accepting the performed changes (before save). */ private List<TreeNode> nodesToDelete = new ArrayList<TreeNode>(); /** * @see Leaf measure */ private List<Measure> measuresToDelete = new ArrayList<Measure>(); public IdentifyRequirements() { this.requiredPlanState = PlanState.RECORDS_CHOSEN; this.correspondingPlanState = PlanState.TREE_DEFINED; } /** * Adds a new Leaf to the given Node. * * @param Node * to attach the Leaf to. * @return New attached Leaf. */ public Leaf addNewLeaf(Node node) { Leaf newLeaf = new Leaf(); node.addChild(newLeaf); // this node has been changed() node.touch(); return newLeaf; } /** * Adds a new Node to the given Node. * * @param Node * to attach the Node to. * @return New attached Node. */ public Node addNewNode(Node node) { Node newNode = new Node(); node.addChild(newNode); // this node has been changed() node.touch(); return newNode; } /** * Method responsible for removing a given node from its objective tree. * * @param node * Node to remove. */ public void removeTreeNode(TreeNode node) { if (node.getParent() != null) { // parent has been changed ((Node) node.getParent()).touch(); ((Node) node.getParent()).removeChild(node); } nodesToDelete.add(node); } /** * Converts a given leaf to a node. * * @param leaf * Leaf to convert. */ public void convertToNode(Leaf leaf) { leaf.getParent().convertToNode(leaf); } /** * Converts a given node to a leaf. * * @param node * Node to convert. */ public void convertToLeaf(Node node) { node.getParent().convertToLeaf(node); } /** * Method responsible for retrieving a copy of a previously uploaded file * including its data. * * @param digitalObject * DigitalObject stored in file system for which the data should * be retrieved. * @return Copy of the given DigitalObject including its data * @throws StorageException * is thrown if any error occurs at retrieving the result file. */ public DigitalObject fetchAttachedFile(DigitalObject digitalObject) throws StorageException { return digitalObjectManager.getCopyOfDataFilledDigitalObject(digitalObject); } /** * Method responsible for attaching additional documentation files. * * @param digitalObject * Digital object to attach * @throws StorageException * is thrown if any error occurs at storing the given file. */ public void attachFile(DigitalObject digitalObject) throws StorageException { digitalObjectManager.moveDataToStorage(digitalObject); plan.getRequirementsDefinition().getUploads().add(digitalObject); plan.getRequirementsDefinition().touch(); addedBytestreams.add(digitalObject.getPid()); } /** * Method responsible for removing a previously attached file. * * @param digitalObject * Digital object to remove. */ public void removeAttachedFile(DigitalObject digitalObject) { plan.getRequirementsDefinition().getUploads().remove(digitalObject); plan.getRequirementsDefinition().touch(); bytestreamsToRemove.add(digitalObject.getPid()); } /** * Method responsible for importing a requirements tree from a given * FreeMind file. * * @param file * FreeMind file to import * @param includesUnits * Indicates if the given FreeMind tree includes units * @return True if import was successful. False otherwise */ public boolean importTreeFromFreeMind(DigitalObject file, boolean includesUnits) { log.debug("Start FreeMind import."); log.debug("FileName: " + file.getFullname()); log.debug("HasUnits is: " + includesUnits); InputStream istream = new ByteArrayInputStream(file.getData().getData()); ObjectiveTree newtree = treeLoader.loadFreeMindObjectiveTree(istream, includesUnits, true); if (newtree == null) { log.error("File is corrupted and new Tree cannot be built out of it."); return false; } // delete old tree nodesToDelete.add(plan.getTree().getRoot()); // set new tree as plan tree plan.getTree().setRoot(newtree.getRoot()); // make sure all scales are set according to measurement infos plan.getTree().adjustScalesToMeasurements(); plan.getTree().setWeightsInitialized(false); log.debug("Imported FreeMind file successfully."); return true; } /** * Method responsible for exporting the current requirements tree as * FreeMind XML. * * @return Current requirements tree in FreeMind XML representation. A * String including the XML-data is returned. */ public String exportTreeAsFreeMindXML() { return projectExporter.exportTreeToFreemind(plan); } /** * Assigns the values of the given measure to the leaf. For this a copy of * the measure is created, the original measure is left unchanged. * * @param measure * @param leaf */ public void assignMeasureToLeaf(final Measure measure, Leaf leaf) { Measure oldMeasure = leaf.getMeasure(); if ((oldMeasure == null) || (!oldMeasure.getUri().equals(measure.getUri()))) { // schedule removal of old measure if (oldMeasure != null) { measuresToDelete.add(oldMeasure); } // and apply the new one leaf.applyMeasure(measure); } } /** * Method responsible for detaching a criterion from a leaf. * * @param leaf * Leaf to detach the criterion from */ public void detachCriterionFromLeaf(Leaf leaf) { leaf.setMeasure(null); leaf.touch(); } @Override protected void saveStepSpecific() { resetTransformers(); resetLeafValues(); // this means the reference to the root has been changed, e.g. by // useTemplate (I think thats the only case, actually) - // so we need to get this ID back (otherwise, each subsequent SAVE will // result in a new entity persist) // simplest way: reload if (plan.getTree().getRoot().getId() == 0) { plan.getTree().setRoot(em.merge(plan.getTree().getRoot())); } prepareChangesForPersist.prepare(plan); saveEntity(plan.getRequirementsDefinition()); saveEntity(plan.getTree()); deleteOrphanedEntities(); } @Override public void discard() throws PlanningException { super.discard(); // We have to extend the standard discard function by the mandatory // clean-up of "nodes to delete" nodesToDelete.clear(); } /** * Resets the transformer of all leaves to the default transformer. * * @see eu.scape_project.planning.IdentifyRequirements.workflow.IdentifyRequirementsAction#resetTransformers() */ private void resetTransformers() { TreeNode root = plan.getTree().getRoot(); for (Leaf leaf : root.getAllLeaves()) { /* * maybe the scaletype is not set yet -> leaf.setDefaultTransformer * has to handle null-values itself */ if ((leaf.getScale() == null) || (leaf.getScale().isDirty())) { leaf.setDefaultTransformer(); } } } /** * Method responsible for resetting leaf values. */ private void resetLeafValues() { for (Leaf leaf : plan.getTree().getRoot().getAllLeaves()) { leaf.resetValues(plan.getAlternativesDefinition().getConsideredAlternatives()); } } /** * deletes orphaned entities which are not removed by persistence provider automatically, namely: * - nodes * - measures */ private void deleteOrphanedEntities() { for (TreeNode n : nodesToDelete) { if (n.getId() != 0) { removeEntity(n); } } nodesToDelete.clear(); for (Measure m : measuresToDelete) { if (m.getId() != 0) { removeEntity(m); } } measuresToDelete.clear(); } public boolean createTreeFromPreservationCase(PreservationCase preservationCase) { ObjectiveTree newTree = new ObjectiveTree(); Node root = new Node(); root.setName(preservationCase.getName()); newTree.setRoot(root); for (ControlPolicy cp : preservationCase.getControlPolicies()) { Measure m = criteriaManager.getMeasure(cp.getMeasure().getUri()); List<String> criteriaHierarchy = criteriaManager.getCategoryHierachy(m.getUri()); Leaf leaf = createLeafInCriteriaHierarchy(newTree.getRoot(), criteriaHierarchy); if (leaf != null) { assignMeasureToLeaf(m, leaf); } log.info(criteriaHierarchy.toString()); } nodesToDelete.add(plan.getTree().getRoot()); // set new tree as plan tree plan.getTree().setRoot(newTree.getRoot()); // make sure all scales are set according to measurement infos plan.getTree().adjustScalesToMeasurements(); plan.getTree().setWeightsInitialized(false); log.debug("Tree created successfully."); return true; } private Leaf createLeafInCriteriaHierarchy(TreeNode node, List<String> criteriaHierarchy) { if (criteriaHierarchy.size() > 0) { for (TreeNode n : node.getChildren()) { if (n.getName().equalsIgnoreCase(criteriaHierarchy.get(0))) { criteriaHierarchy.remove(0); return createLeafInCriteriaHierarchy(n, criteriaHierarchy); } } Node newNode = new Node(); newNode.setName(criteriaHierarchy.get(0)); ((Node) node).addChild(newNode); criteriaHierarchy.remove(0); return createLeafInCriteriaHierarchy(newNode, criteriaHierarchy); } else { Leaf leaf = new Leaf(); ((Node) node).addChild(leaf); return leaf; } } }