Restructured the project creating a router directory for the router bundle implementa...
[xmlrouter] / router / impl / src / main / java / org / wamblee / xmlrouter / impl / TransformationPaths.java
diff --git a/router/impl/src/main/java/org/wamblee/xmlrouter/impl/TransformationPaths.java b/router/impl/src/main/java/org/wamblee/xmlrouter/impl/TransformationPaths.java
new file mode 100644 (file)
index 0000000..e6956d3
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2005-2011 the original author or authors.
+ * 
+ * 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 org.wamblee.xmlrouter.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.wamblee.xmlrouter.config.Transformation;
+
+/**
+ * This class manages transformations and computes the shortest transformations
+ * paths based on the provided transformations.
+ * 
+ * @author Erik Brakkee
+ * 
+ */
+public class TransformationPaths {
+
+    private Collection<Transformation> transformations;
+    private List<String> vertices;
+    private TransformationPath[][] matrix;
+
+    /**
+     * Construct the transformations.
+     */
+    public TransformationPaths(Collection<Transformation> aTransformations) {
+        transformations = aTransformations;
+        vertices = new ArrayList<String>();
+        matrix = new TransformationPath[0][0];
+        computeTransformationSequences();
+    }
+
+    /**
+     * Gets the possible target types based on an input type.
+     * 
+     * @param aType
+     *            Input type.
+     * @return Possible target types.
+     */
+    public Collection<String> getPossibleTargetTypes(String aType) {
+        int index = vertices.indexOf(aType);
+        Set<String> res = new HashSet<String>();
+        for (int j = 0; j < vertices.size(); j++) {
+            if (matrix[index][j] != null) {
+                String value = matrix[index][j].getToType();
+                if (value == null) {
+                    value = aType;
+                }
+                res.add(value);
+            }
+        }
+        res.add(aType);
+        return res;
+    }
+
+    /**
+     * Gets the transformation path from A to B.
+     * 
+     * @param aFrom
+     *            From
+     * @param aTo
+     *            To
+     * @return Transformatkon path or null if not found.
+     */
+    public TransformationPath getPath(String aFrom, String aTo) {
+        int i = vertices.indexOf(aFrom);
+        if (i < 0) {
+            if (aFrom.equals(aTo)) {
+                return new TransformationPath();
+            }
+            return null;
+        }
+
+        int j = vertices.indexOf(aTo);
+        if (j < 0) {
+            return null;
+        }
+        return matrix[i][j];
+    }
+
+    /**
+     * Computest the transformation sequences using Floyd's algorithm.
+     */
+    private void computeTransformationSequences() {
+        vertices = new ArrayList<String>();
+
+        // Obtain possible starting points.
+        Set<String> v = new HashSet<String>();
+        for (Transformation transformation : transformations) {
+            v.add(transformation.getFromType());
+            v.add(transformation.getToType());
+        }
+
+        vertices.addAll(v);
+
+        matrix = new TransformationPath[vertices.size()][vertices.size()];
+
+        // Floyd's algorithm.
+        int nvertices = vertices.size();
+        for (int i = 0; i < nvertices; i++) {
+            matrix[i][i] = new TransformationPath();
+        }
+        for (Transformation transformation : transformations) {
+            int from = vertices.indexOf(transformation.getFromType());
+            int to = vertices.indexOf(transformation.getToType());
+            TransformationPath path = new TransformationPath(transformation);
+            matrix[from][to] = path;
+        }
+
+        for (int k = 0; k < nvertices; k++) {
+            for (int i = 0; i < nvertices; i++) {
+                for (int j = 0; j < nvertices; j++) {
+                    // if the path from i to j through k is shorter then the
+                    // existing path then
+                    // replace it.
+                    int lij = getPathLength(i, j);
+                    int lik = getPathLength(i, k);
+                    int lkj = getPathLength(k, j);
+                    if (lik + lkj < lij) {
+                        matrix[i][j] = matrix[i][k].appendPath(matrix[k][j]);
+                    }
+                }
+            }
+        }
+    }
+
+    private int getPathLength(int i, int j) {
+        // We use MAX_INT/3 as infinity. This ensures that the default integer
+        // comparison in Floyd's algorithm does not lead to overflow.
+        return matrix[i][j] == null ? Integer.MAX_VALUE / 3 : matrix[i][j]
+            .size();
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("Transformations(");
+        int nvertices = vertices.size();
+        for (int i = 0; i < nvertices; i++) {
+            for (int j = 0; j < nvertices; j++) {
+                TransformationPath path = matrix[i][j];
+                if (path != null) {
+                    buf.append(vertices.get(i));
+                    buf.append(" -> ");
+                    buf.append(vertices.get(j));
+                    buf.append(": ");
+                    buf.append(path);
+                    buf.append("\n");
+                }
+            }
+        }
+        buf.append(")");
+        return buf.toString();
+    }
+}