/* * 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 transformations; private List vertices; private TransformationPath[][] matrix; /** * Construct the transformations. */ public TransformationPaths(Collection aTransformations) { transformations = aTransformations; vertices = new ArrayList(); 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 getPossibleTargetTypes(String aType) { int index = vertices.indexOf(aType); Set res = new HashSet(); 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(); // Obtain possible starting points. Set v = new HashSet(); 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(); } }