/*
* Copyright (C) Yutaka Matsuno 2010-2012 All rights reserved.
*/
package net.dependableos.dcase.diagram.editor.logic.findandreplace;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.swt.widgets.Text;
import net.dependableos.dcase.Argument;
import net.dependableos.dcase.BasicNode;
import net.dependableos.dcase.diagram.common.model.AttributeType;
import net.dependableos.dcase.diagram.common.model.LinkInfo;
import net.dependableos.dcase.diagram.common.util.LinkManager;
import net.dependableos.dcase.diagram.common.util.NumberUtil;
import net.dependableos.dcase.diagram.common.util.StringUtil;
import net.dependableos.dcase.diagram.edit.parts.ArgumentEditPart;
import net.dependableos.dcase.diagram.edit.parts.custom.DcaseNodeEditPart;
import net.dependableos.dcase.diagram.editor.common.util.DcaseEditorUtil;
import net.dependableos.dcase.diagram.part.DcaseDiagramEditor;
import net.dependableos.dcase.diagram.part.DcaseDiagramEditorUtil;
/**
* Executes the find and replace for the node list.
*/
public class FindAndReplaceLogic {
/**
* The value of non ordered.
*/
private static final int NON_NUMBER = Integer.MAX_VALUE;
/**
* The list of attributes of the search object.
*/
private static final AttributeType[] TARGET_ATTRIBUTES =
new AttributeType[] { AttributeType.NAME, AttributeType.DESC };
/**
* The target D-Case diagram editor.
*/
private DcaseDiagramEditor dcaseDiagramEditor = null;
/**
* The target ArgumentEditPart object.
*/
private ArgumentEditPart argumentEditPart = null;
/**
* The LinkManager object.
*/
private LinkManager linkManager = null;
/**
* The BasicNode list.
*/
private List<BasicNode> searchNodeList;
/**
* The ID of the node which is hit by search.
*/
private String selectedNodeId = null;
/**
* The current index within the node list.
*/
private int currentNodeIndex = 0;
/**
* The index within the attribute of the substring which is hit.
*/
private int foundDescIndex = 0;
/**
* The first index number that starts to search.
*/
private int startNodeIndex = 0;
/**
* A string to find.
*/
private String findString = StringUtil.EMPTY;
/**
* The index within the attribute which starts the next search.
*/
private int startDescIndex = 0;
/**
* The current index of TARGET_ATTRIBUTES list.
*/
private int currentAttributeIndex = 0;
/**
* The object to manage the replace handling of 'Desc' attribute and 'DescFormat' attribute.
*/
private DescReplaceManager descReplaceManager;
/**
* Allocates a FindAndReplaceLogic object and initializes it to represent
* the DcaseDiagramEditor object.
*
* @param dcaseDiagramEditor The current diagram.
*/
public FindAndReplaceLogic(DcaseDiagramEditor dcaseDiagramEditor) {
this.dcaseDiagramEditor = dcaseDiagramEditor;
argumentEditPart = DcaseEditorUtil.getCurrentArgumentEditPart(dcaseDiagramEditor);
Argument argument = (Argument) dcaseDiagramEditor.getDiagram().getElement();
// creates link manager.
linkManager = new LinkManager();
linkManager.load((XMLResource) argument.eResource());
findSelectedNodeId();
// creates the Global Parameter list.
descReplaceManager = new DescReplaceManager(argumentEditPart, argument.getParameterVals());
if (this.argumentEditPart != null) {
createSearchNodeList();
}
}
/**
* Creates the searched node list.
*/
private void createSearchNodeList() {
Set<String> allIdSet = linkManager.getAllNodes();
List<String> allIdList = new ArrayList<String>();
Map<String, List<String>> nodeTreeMap = new HashMap<String, List<String>>();
List<String> topNodeIdList = new ArrayList<String>();
searchNodeList = new ArrayList<BasicNode>();
for (String id : allIdSet) {
if (!(linkManager.getBasicNode(id) instanceof Argument)) {
allIdList.add(id);
List<String> sourceIdList = linkManager.getSource(id);
if (sourceIdList == null || sourceIdList.isEmpty()) {
// adds a top node
topNodeIdList.add(id);
}
}
}
// creates the tree information from top node
for (String topNodeId : topNodeIdList) {
List<String> treeIdList = new ArrayList<String>();
treeIdList.add(topNodeId);
searchTree(topNodeId, treeIdList);
nodeTreeMap.put(topNodeId, treeIdList);
}
// removes the node ID that the tree information is included in
for (Map.Entry<String, List<String>> entry : nodeTreeMap.entrySet()) {
List<String> nodeIdList = entry.getValue();
for (String id : nodeIdList) {
if (allIdList.contains(id)) {
allIdList.remove(id);
}
}
}
// the node ID left in the list is a circulation tree
if (!allIdList.isEmpty()) {
String topNodeId = allIdList.get(0);
List<String> treeIdList = new ArrayList<String>();
treeIdList.add(topNodeId);
topNodeIdList.add(topNodeId);
searchTree(topNodeId, treeIdList);
nodeTreeMap.put(topNodeId, treeIdList);
}
// sorts the list of top node
topNodeIdList = sortTopNodeList(topNodeIdList);
// creates the search list
List<String> searchNodeIdList = new ArrayList<String>();
searchNodeList = new ArrayList<BasicNode>();
for (String topNodeId : topNodeIdList) {
List<String> nodeIdList = nodeTreeMap.get(topNodeId);
for (String nodeId : nodeIdList) {
if (!searchNodeIdList.contains(nodeId)) {
BasicNode basicNode = linkManager.getBasicNode(nodeId);
searchNodeList.add(basicNode);
searchNodeIdList.add(nodeId);
}
}
}
// sets the index number to start searching at
if (!StringUtil.isNullOrEmpty(selectedNodeId)) {
startNodeIndex = searchNodeIdList.indexOf(selectedNodeId);
}
currentNodeIndex = startNodeIndex;
}
/**
* Sorts the list of the top node by the x coordinate.
* @param nodeIdList The list of the top node
* @return The list that was sorted
*/
private List<String> sortTopNodeList(List<String> nodeIdList) {
List<String> resultNodeIdList = new ArrayList<String>();
Map<String, Rectangle> map = new HashMap<String, Rectangle>();
for (String id : nodeIdList) {
BasicNode basicNode = linkManager.getBasicNode(id);
EditPart editPart = argumentEditPart.findEditPart(null, basicNode);
map.put(id, ((GraphicalEditPart) editPart).getFigure().getBounds());
}
// sorts it by the x-origin
List<Map.Entry<String, Rectangle>> entries =
new ArrayList<Map.Entry<String, Rectangle>>(map.entrySet());
Collections.sort(entries, new Comparator<Map.Entry<String, Rectangle>>() {
public int compare(Map.Entry<String, Rectangle> o1, Map.Entry<String, Rectangle> o2) {
Map.Entry<String, Rectangle> e1 = (Map.Entry<String, Rectangle>) o1;
Map.Entry<String, Rectangle> e2 = (Map.Entry<String, Rectangle>) o2;
int compareToResult = ((Integer) e1.getValue().x).compareTo((Integer) e2.getValue().x);
if (compareToResult != 0) {
return compareToResult;
}
return ((Integer) e1.getValue().y).compareTo((Integer) e2.getValue().y);
}
});
for (Map.Entry<String, Rectangle> entry : entries) {
resultNodeIdList.add(entry.getKey());
}
return resultNodeIdList;
}
/**
* Search for the tree and creates the node list.
* @param nodeId The node ID of start point
* @param treeIdList The list of a node included in the tree
*/
private void searchTree(String nodeId, List<String> treeIdList) {
List<String> childNodeIdList = linkManager.getTarget(nodeId);
if (childNodeIdList == null || childNodeIdList.isEmpty()) {
return;
}
// compares brother nodes
List<String> addToChildNodeIdList = compareSiblingNode(nodeId, childNodeIdList);
for (String id : addToChildNodeIdList) {
if (!treeIdList.contains(id)) {
treeIdList.add(id);
searchTree(id, treeIdList);
}
}
}
/**
* Sorts by the sibling order.
* @param sourceId The ID of the parent node
* @param targetIdList The node ID list of the node
* @return The node ID list which was sorted
*/
private List<String> compareSiblingNode(String sourceId, List<String> targetIdList) {
List<String> nodeList = new ArrayList<String>();
// compares the value of the 'Sibling Order' attribute of Link
if (!StringUtil.isNullOrEmpty(sourceId)) {
Set<String> allLinkIds = linkManager.getAllLinks();
Map<String, Integer> siblingOrderMap = new HashMap<String, Integer>();
for (String linkId : allLinkIds) {
LinkInfo linkInfo = linkManager.getLinkInfo(linkId);
// deletes the '#' character.
String linkSourceId = (String) linkInfo.getAttribute(AttributeType.SOURCE);
if (linkSourceId.equals(sourceId)) {
String linkTargetId = (String) linkInfo.getAttribute(AttributeType.TARGET);
if (targetIdList.contains(linkTargetId)) {
int siblingOrder = NumberUtil.parseIntWithDefault(
(String) linkInfo.getAttribute(AttributeType.SIBLINGORDER), NON_NUMBER);
if (siblingOrder != NON_NUMBER) {
siblingOrderMap.put(linkTargetId, siblingOrder);
}
}
}
}
// sorts the value of the 'Sibling Order' attribute.
List<Map.Entry<String, Integer>> entries =
new ArrayList<Map.Entry<String, Integer>>(siblingOrderMap.entrySet());
Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
Map.Entry<String, Integer> e1 = (Map.Entry<String, Integer>) o1;
Map.Entry<String, Integer> e2 = (Map.Entry<String, Integer>) o2;
return (e1.getValue()).compareTo(e2.getValue());
}
});
for (Map.Entry<String, Integer> entry : entries) {
nodeList.add(entry.getKey());
targetIdList.remove(entry.getKey());
}
}
// 'Sibling Order' attribute: not set
for (String id : targetIdList) {
nodeList.add(id);
}
return nodeList;
}
/**
* Searches the node ID that was selected in diagram.
*/
@SuppressWarnings("unchecked")
private void findSelectedNodeId() {
List editPartList = argumentEditPart.getChildren();
selectedNodeId = StringUtil.EMPTY;
for (Object editPart : editPartList) {
if (editPart instanceof DcaseNodeEditPart) {
if (((DcaseNodeEditPart) editPart).getSelected() == EditPart.SELECTED_PRIMARY) {
selectedNodeId = DcaseEditorUtil
.getUUIDs((DcaseNodeEditPart) editPart);
break;
}
}
}
}
/**
* Searches the next.
* @return The BasicNode object that was found
*/
public BasicNode findNext() {
// the index number of node in diagram
int searchIndex = currentNodeIndex;
BasicNode node = null;
do {
BasicNode currentNode = searchNodeList.get(searchIndex);
boolean isFound = false;
for (int i = currentAttributeIndex; i < TARGET_ATTRIBUTES.length; i++) {
String attributeValue = StringUtil.EMPTY;
if (TARGET_ATTRIBUTES[i] == AttributeType.NAME) {
attributeValue = currentNode.getName();
} else if (TARGET_ATTRIBUTES[i] == AttributeType.DESC) {
attributeValue = currentNode.getDesc();
}
if (!StringUtil.isNullOrEmpty(attributeValue)) {
attributeValue = attributeValue.toLowerCase();
int findStringIndex = attributeValue.indexOf(findString.toLowerCase(), startDescIndex);
if (findStringIndex >= 0) {
foundDescIndex = findStringIndex;
startDescIndex = foundDescIndex + 1;
currentNodeIndex = searchIndex;
currentAttributeIndex = i;
if (TARGET_ATTRIBUTES[i] == AttributeType.DESC) {
// sets the target BasicNode
descReplaceManager.setTargetBasicNode(currentNode);
}
node = currentNode;
isFound = true;
break;
}
}
foundDescIndex = 0;
startDescIndex = 0;
}
if (isFound) {
break;
}
searchIndex++;
if (searchIndex >= searchNodeList.size()) {
searchIndex = 0;
}
currentAttributeIndex = 0;
} while (searchIndex != startNodeIndex);
if (searchIndex == startNodeIndex) {
currentNodeIndex = startNodeIndex;
}
return node;
}
/**
* Replaces the substring which is hit with the new string.
* @param replaceWith The string which replaces
* @param descText the text control of desc
* @return True when the replace is run
*/
public boolean replace(String replaceWith, Text descText) {
BasicNode currentNode = getCurrentBasicNode();
String description = currentNode.getDesc();
String sourceDesc = description.substring(
foundDescIndex, foundDescIndex + findString.length());
if (findString.equalsIgnoreCase(sourceDesc)) {
if (descReplaceManager.replace(foundDescIndex,
foundDescIndex + findString.length(), replaceWith)) {
if (descText != null) {
descText.setText(currentNode.getDesc());
descText.setSelection(foundDescIndex, foundDescIndex + replaceWith.length());
}
return true;
}
}
return false;
}
/**
* The BasicNode that hits the finding.
* @return The found BasicNode object
*/
public BasicNode getCurrentBasicNode() {
if (currentNodeIndex < 0 && currentNodeIndex >= searchNodeList.size()) {
return null;
}
return searchNodeList.get(currentNodeIndex);
}
/**
* Cursors the BasicNode to the diagram.
* @param basicNode The BasicNode that cursors
*/
public void selectElementsInDiagram(BasicNode basicNode) {
EditPart currentEditPart = argumentEditPart.findEditPart(null, basicNode);
DcaseDiagramEditorUtil.selectElementsInDiagram(dcaseDiagramEditor, Arrays
.asList(new EditPart[] { currentEditPart }));
}
/**
* Returns the size of searching BasicNode list.
* @return The size of list
*/
public int getSearchNodeListSize() {
return searchNodeList.size();
}
/**
* Sets the find string.
* @param findString the find string.
*/
public void setFindString(String findString) {
if (!StringUtil.isNullOrEmpty(findString)) {
this.findString = findString;
} else {
this.findString = StringUtil.EMPTY;
}
reset();
}
/**
* Returns the position of string that was found the value of the attribute.
* @return The position of string
*/
public int getFoundPosition() {
return foundDescIndex;
}
/**
* Returns the AttributeType that was found the value of the attribute.
* @return The AttributeType
*/
public AttributeType getFoundAttributeType() {
return TARGET_ATTRIBUTES[currentAttributeIndex];
}
/**
* Resets the search condition.
*/
public void reset() {
startNodeIndex = currentNodeIndex;
foundDescIndex = 0;
startDescIndex = 0;
currentAttributeIndex = 0;
}
/**
* True if replace is possible.
* @return True if replace is possible.
*/
public boolean isReplacePossible() {
return descReplaceManager.isReplacePossible(foundDescIndex,
foundDescIndex + findString.length());
}
}