// ============================================================================
//
// Copyright (C) 2006-2012 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package org.talend.core.model.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.talend.core.model.components.IComponent;
import org.talend.core.model.metadata.IMetadataTable;
import org.talend.core.model.process.EConnectionType;
import org.talend.core.model.process.ElementParameterParser;
import org.talend.core.model.process.IConnection;
import org.talend.core.model.process.IConnectionCategory;
import org.talend.core.model.process.INode;
/**
* DOC xtan class global comment. Detailled comment <br/>
*
*/
public class NodeUtil {
/**
* DOC sort the outgoingconnections to make sure the first connection is EConnectionType.FLOW_MAIN or
* EConnectionType.FLOW_REF<br/>
* <p>
* bug:9363, if a component have 2 output links,
* <li>"EConnectionType.FLOW_MAIN(FLOW), EConnectionType.FLOW_REF(REJECT)"</li>
* <li>"EConnectionType.FLOW_MAIN(REJECT), EConnectionType.FLOW_REF(FLOW)"</li>, make FLOW before "REJECT"
* </p>
*
* @param node
* @return List<? extends IConnection>
*/
public static List<? extends IConnection> getOutgoingCamelSortedConnections(INode node) {
List<IConnection> conns = null;
List<? extends IConnection> outgoingConnections = node.getOutgoingConnections();
if (outgoingConnections != null) {
conns = new ArrayList<IConnection>(outgoingConnections);
Collections.sort(conns, new Comparator<IConnection>() {
public int compare(IConnection o1, IConnection o2) {
if (EConnectionType.ROUTE_WHEN == o1.getLineStyle())
return -1;
if (EConnectionType.ROUTE_OTHER == o1.getLineStyle())
if (EConnectionType.ROUTE_WHEN == o2.getLineStyle())
return 1;
if (EConnectionType.ROUTE_ENDBLOCK == o1.getLineStyle()) {
if (EConnectionType.ROUTE_WHEN == o2.getLineStyle() || EConnectionType.ROUTE_OTHER == o2.getLineStyle())
return 2;
if (EConnectionType.ROUTE_TRY == o2.getLineStyle() || EConnectionType.ROUTE_CATCH == o2.getLineStyle()
|| EConnectionType.ROUTE_FINALLY == o2.getLineStyle())
return 3;
if (EConnectionType.ROUTE == o2.getLineStyle()) {
return 4;
}
}
if (EConnectionType.ROUTE_TRY == o1.getLineStyle())
return -1;
if (EConnectionType.ROUTE_CATCH == o1.getLineStyle())
if (EConnectionType.ROUTE_TRY == o2.getLineStyle())
return 1;
if (EConnectionType.ROUTE_FINALLY == o1.getLineStyle())
if (EConnectionType.ROUTE_TRY == o2.getLineStyle() || EConnectionType.ROUTE_CATCH == o2.getLineStyle())
return 2;
return 0;
}
});
}
return conns;
}
public static List<? extends IConnection> getOutgoingSortedConnections(INode node) {
List<IConnection> conns = null;
List<? extends IConnection> outgoingConnections = node.getOutgoingConnections();
if (outgoingConnections != null) {
conns = new ArrayList<IConnection>(outgoingConnections);
Collections.sort(conns, new Comparator<IConnection>() {
public int compare(IConnection connection1, IConnection connection2) {
EConnectionType lineStyle = connection1.getLineStyle();
EConnectionType lineStyle2 = connection2.getLineStyle();
// "FLOW" only for the default Main connection, if user define his connection like: "FILTER",
// "REJECT", "FLOW", he should use this API in JET directly: List<? extends IConnection> connsFilter
// = node.getOutgoingConnections("FILTER");
// 1. FLOW first
if ("FLOW".equals(connection1.getConnectorName())) { //$NON-NLS-1$
return -1;
}
if ("FLOW".equals(connection2.getConnectorName())) { //$NON-NLS-1$
return 1;
}
// 2. FLOW_MAIN, FLOW_MERGE second
if (lineStyle == EConnectionType.FLOW_MAIN || lineStyle == EConnectionType.FLOW_MERGE) {
return -1;
}
if (lineStyle2 == EConnectionType.FLOW_MAIN || lineStyle2 == EConnectionType.FLOW_MERGE) {
return 1;
}
// 3. others cases, the last
return 0;
}
});
}
return conns;
}
/**
* DOC get the EConnectionType.FLOW_MAIN or EConnectionType.FLOW_REF out goning Connections<br/>
*
* @param node
* @return List<? extends IConnection>
*/
public static List<? extends IConnection> getMainOutgoingConnections(INode node) {
List<IConnection> conns = null;
List<? extends IConnection> outgoingConnections = node.getOutgoingConnections();
if (outgoingConnections != null) {
conns = new ArrayList<IConnection>();
for (int i = 0; i < outgoingConnections.size(); i++) {
IConnection connection = outgoingConnections.get(i);
if (connection.getLineStyle() == EConnectionType.FLOW_MAIN
|| connection.getLineStyle() == EConnectionType.FLOW_REF) {
conns.add(connection);
}
}
}
return conns;
}
public static List<? extends IConnection> getOutgoingConnections(INode node, EConnectionType connectionType) {
List<IConnection> conns = null;
List<? extends IConnection> outgoingConnections = node.getOutgoingConnections();
if (outgoingConnections != null) {
conns = new ArrayList<IConnection>();
for (int i = 0; i < outgoingConnections.size(); i++) {
IConnection connection = outgoingConnections.get(i);
if (connection.getLineStyle() == connectionType) {
conns.add(connection);
}
}
}
return conns;
}
public static List<? extends IConnection> getIncomingConnections(INode node, EConnectionType connectionType) {
List<IConnection> conns = null;
List<? extends IConnection> incomingConnections = node.getIncomingConnections();
if (incomingConnections != null) {
conns = new ArrayList<IConnection>();
for (int i = 0; i < incomingConnections.size(); i++) {
IConnection connection = incomingConnections.get(i);
if (connection.getLineStyle() == connectionType) {
conns.add(connection);
}
}
}
return conns;
}
public static List<? extends IConnection> getOutgoingConnections(INode node, String connectorName) {
List<IConnection> conns = null;
List<? extends IConnection> outgoingConnections = node.getOutgoingConnections();
if (outgoingConnections != null) {
conns = new ArrayList<IConnection>();
for (int i = 0; i < outgoingConnections.size(); i++) {
IConnection connection = outgoingConnections.get(i);
if (connectorName.equals(connection.getConnectorName())) {
conns.add(connection);
}
}
}
return conns;
}
/**
*
* wzhang Comment method "getIncomingConnections".
*
* @param node
* @param category
* @return
*/
public static List<? extends IConnection> getIncomingConnections(INode node, int category) {
List<IConnection> conns = null;
List<? extends IConnection> incomingConnections = node.getIncomingConnections();
if (incomingConnections != null) {
conns = new ArrayList<IConnection>();
for (int i = 0; i < incomingConnections.size(); i++) {
IConnection connection = incomingConnections.get(i);
if (connection.getLineStyle().hasConnectionCategory(category)) {
conns.add(connection);
}
}
}
return conns;
}
public static List<? extends IConnection> getOutgoingConnections(INode node, int category) {
List<IConnection> conns = null;
List<? extends IConnection> outgoingConnections = node.getOutgoingConnections();
if (outgoingConnections != null) {
conns = new ArrayList<IConnection>();
for (int i = 0; i < outgoingConnections.size(); i++) {
IConnection connection = outgoingConnections.get(i);
if (connection.getLineStyle().hasConnectionCategory(category)) {
conns.add(connection);
}
}
}
return conns;
}
public static List<IMetadataTable> getIncomingMetadataTable(INode node, int category) {
List<IMetadataTable> metadatas = new ArrayList<IMetadataTable>();
List<? extends IConnection> incomingConnections = node.getIncomingConnections();
if (incomingConnections != null) {
for (int i = 0; i < incomingConnections.size(); i++) {
IConnection connection = incomingConnections.get(i);
if (connection.getLineStyle().hasConnectionCategory(category)) {
metadatas.add(connection.getMetadataTable());
}
}
}
return metadatas;
}
public static List<? extends IConnection> getIncomingConnections(INode node, String connectorName) {
List<IConnection> conns = null;
List<? extends IConnection> incomingConnections = node.getIncomingConnections();
if (incomingConnections != null) {
conns = new ArrayList<IConnection>();
for (int i = 0; i < incomingConnections.size(); i++) {
IConnection connection = incomingConnections.get(i);
if (connectorName.equals(connection.getConnectorName())) {
conns.add(connection);
}
}
}
return conns;
}
public static boolean checkConnectionAfterNode(INode node, EConnectionType connectionType, List<INode> checkedNodes) {
// fix bug 0004935: Error on job save
if (checkedNodes.contains(node)) {
return false;
} else {
checkedNodes.add(node);
}
boolean result = false;
List<? extends IConnection> onErrorConns = getOutgoingConnections(node, EConnectionType.ON_COMPONENT_ERROR);
if (onErrorConns == null || onErrorConns.size() == 0) {
List<? extends IConnection> conns = getOutgoingSortedConnections(node);
if (conns != null && conns.size() > 0) {
for (IConnection conn : conns) {
result = checkConnectionAfterNode(conn.getTarget(), EConnectionType.ON_COMPONENT_ERROR, checkedNodes);
if (result) {
break;
}
}
} else {
result = false;
}
} else {
result = true;
}
return result;
}
public static boolean checkComponentErrorConnectionAfterNode(INode node) {
List<INode> checkedNodes = new ArrayList<INode>();
return checkConnectionAfterNode(node, EConnectionType.ON_COMPONENT_ERROR, checkedNodes);
}
/**
* DOC xtan
* <p>
* InLineJob means all nodes after a iterate link(The nodes will execute many times on every iterate).
* </p>
* Notice: The search method don't consider the second branch of the tUnite, but it is ok.
*
* @param node
* @return
*/
public static Set<? extends IConnection> getAllInLineJobConnections(INode node) {
Set<String> uniqueNamesDone = new HashSet<String>();
uniqueNamesDone.add(node.getUniqueName());
return getAllInLineJobConnections(node, uniqueNamesDone);
}
private static Set<? extends IConnection> getAllInLineJobConnections(INode node, Set<String> uniqueNamesDone) {
Set<IConnection> conns = new HashSet<IConnection>();
List<? extends IConnection> outgoingConnections = node.getOutgoingConnections();
if (outgoingConnections != null) {
conns.addAll(outgoingConnections); // add all
for (int i = 0; i < outgoingConnections.size(); i++) {
IConnection connection = outgoingConnections.get(i);
INode nextNode = connection.getTarget();
if (!uniqueNamesDone.contains(nextNode.getUniqueName())) {
uniqueNamesDone.add(nextNode.getUniqueName());
conns.addAll(getAllInLineJobConnections(nextNode, uniqueNamesDone)); // follow this way
}
}
}
return conns;
}
public static INode getFirstMergeNode(INode node) {
INode mergeNode = null;
for (IConnection connection : node.getOutgoingConnections()) {
if (connection.getLineStyle().hasConnectionCategory(IConnectionCategory.MERGE)) {
mergeNode = connection.getTarget();
} else if (connection.getLineStyle().hasConnectionCategory(IConnectionCategory.FLOW)) {
mergeNode = getFirstMergeNode(connection.getTarget());
}
if (mergeNode != null) {
break;
}
}
return mergeNode;
}
public static boolean isLastFromMergeTree(INode node) {
INode firstMergeNode = getFirstMergeNode(node);
int totMerge = NodeUtil.getIncomingConnections(firstMergeNode, IConnectionCategory.MERGE).size();
Integer posMerge = node.getLinkedMergeInfo().get(firstMergeNode);
return totMerge == posMerge;
}
public static IConnection getRealConnectionTypeBased(IConnection connection) {
IConnection realConnection = connection;
boolean connectionAvailable = true;
// while (connectionAvailable && realConnection.getSource().getComponent().getName().equals("tReplace")) { //$NON-NLS-1$
while (connectionAvailable && !realConnection.getSource().isSubProcessStart()
&& realConnection.getSource().getComponent().isDataAutoPropagated()) {
List<IConnection> inConnections = (List<IConnection>) getIncomingConnections(realConnection.getSource(),
IConnectionCategory.FLOW);
if (inConnections.size() > 0) {
realConnection = inConnections.get(0);
} else {
connectionAvailable = false;
}
}
return realConnection;
}
/**
* DOC wliu
* <p>
* judge if the current connection is the last output connection of the component
* </p>
* Notice: It is used in subtree_end.javajet. And the aim is for feature5996
*
* @param connection
* @return
*/
public static boolean isLastMultiplyingOutputComponents(IConnection connection) {
List<? extends IConnection> conns = connection.getSource().getOutgoingConnections();
int last = 0;
if (conns != null && conns.size() > 0) {
for (int i = 0; i < conns.size(); i++) {
if (conns.get(i).getLineStyle().hasConnectionCategory(IConnectionCategory.DATA)) {
last = i;
}
}
}
if (connection.getName().equals(conns.get(last).getName())) {
return true;
}
return false;
}
public static Map<INode, Integer> getLinkedMergeInfo(final INode node) {
Map<INode, Integer> map = new HashMap<INode, Integer>();
getLinkedMergeInfo(node, map);
if (map.isEmpty()) {
// in case the component is not linked directly, it should take the status of previous component, since it
// will be in the same branch.
getLinkedMergeInfoFromMainLink(node, map);
}
return map;
}
private static void getLinkedMergeInfoFromMainLink(final INode node, final Map<INode, Integer> map) {
if (node.getComponent().useMerge()) {
return;
}
List<IConnection> inputConnections = (List<IConnection>) getIncomingConnections(node, IConnectionCategory.MAIN);
if (inputConnections.size() > 0) {
IConnection input = inputConnections.get(0);
INode sourceNode = input.getSource();
if (sourceNode.getJobletNode() != null) {
sourceNode = sourceNode.getJobletNode();
}
getLinkedMergeInfo(sourceNode, map);
if (map.isEmpty()) {
getLinkedMergeInfoFromMainLink(sourceNode, map);
}
}
}
private static void getLinkedMergeInfo(final INode node, final Map<INode, Integer> map) {
List<? extends IConnection> outgoingConnections = node.getOutgoingConnections();
for (int i = 0; (i < outgoingConnections.size()); i++) {
IConnection connec = outgoingConnections.get(i);
if (connec.isActivate()) {
if (connec.getLineStyle().hasConnectionCategory(EConnectionType.MERGE)) {
INode jNode = connec.getTarget();
if (jNode.getJobletNode() != null) {
jNode = jNode.getJobletNode();
}
map.put(jNode, connec.getInputId());
getLinkedMergeInfo(jNode, map);
} else if (connec.getLineStyle().hasConnectionCategory(EConnectionType.MAIN) && connec.getTarget() != null) {
INode jNode = connec.getTarget();
if (jNode.getJobletNode() != null) {
jNode = jNode.getJobletNode();
}
getLinkedMergeInfo(jNode, map);
}
}
}
}
// this is only for bug:11754, and only be used in generating code.
public static boolean isDataAutoPropagated(final INode node) {
IComponent component = node.getComponent();
// if it is tJavaFlex, use the property Version_V2_0 to instead the DATA_AUTO_PROPAGATE="false" config
// in tJavaFlex_java.xml
if (component.getName().compareTo("tJavaFlex") == 0) {
boolean isVersionV20 = "true".equals(ElementParameterParser.getValue(node, "__Version_V2_0__"));
return isVersionV20;
} else {
return component.isDataAutoPropagated();
}
}
}