/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.utility.string; /** * This partial string comparator engine performs a brute-force matching of * the strings in the two sets of string holders. Every string in the first set is * compared with every string in the second set and the returned matches * are those with the highest scores. This engine may not provide timely * performance with large sets of strings. */ public class ExhaustivePartialStringComparatorEngine implements PartialStringComparatorEngine { private final PartialStringComparator partialStringComparator; public ExhaustivePartialStringComparatorEngine(PartialStringComparator partialStringComparator) { super(); this.partialStringComparator = partialStringComparator; } /** * @see PartialStringComparatorEngine#match(StringHolder[], StringHolder[]) */ public StringHolderPair[] match(StringHolder[] stringHolders1, StringHolder[] stringHolders2) { InternalStringHolderPair[] stringHolderPairs = this.buildStringHolderPairs(stringHolders1, stringHolders2); this.calculateScores(stringHolderPairs, stringHolders2); this.setString2(stringHolderPairs, stringHolders2); return stringHolderPairs; } /** * Build enough string holder pairs to hold all the string holders in the specified sets * and initialize them with the string holders from the first set. */ private InternalStringHolderPair[] buildStringHolderPairs(StringHolder[] stringHolders1, StringHolder[] stringHolders2) { int len1 = stringHolders1.length; int len2 = stringHolders2.length; // build up the string pairs int len = Math.max(len1, len2); InternalStringHolderPair[] stringHolderPairs = new InternalStringHolderPair[len]; for (int i = 0; i < len1; i++) { stringHolderPairs[i] = new SimpleInternalStringHolderPair(stringHolders1[i], len2); } if (len1 < len2) { for (int i = len1; i < len2; i++) { stringHolderPairs[i] = new NullInternalStringHolderPair(); } } return stringHolderPairs; } /** * Loop through the partially-populated string holder pairs (at this point they * hold only string holders from the first set of string holders) and calculate the * scores for matching the pair's string from the first set of string holders with * *every* string in the second set. */ private void calculateScores(InternalStringHolderPair[] stringHolderPairs, StringHolder[] stringHolders2) { PartialStringComparator psc = this.partialStringComparator; for (int i = stringHolderPairs.length; i-- > 0; ) { stringHolderPairs[i].calculateScores(psc, stringHolders2); } } /** * Loop through the still partially-populated string holder pairs and determine * which string holder from the first set of string holders each string holder * from the second set should be assigned to. */ private void setString2(InternalStringHolderPair[] stringHolderPairs, StringHolder[] stringHolders2) { // this copy will be cleared out as we match all the string pairs int stringHolderPairsLength = stringHolderPairs.length; InternalStringHolderPair[] stringHolderPairsCopy = new InternalStringHolderPair[stringHolderPairsLength]; System.arraycopy(stringHolderPairs, 0, stringHolderPairsCopy, 0, stringHolderPairsLength); // this copy will also be cleared out as we match all the string holder pairs int stringHolders2Length = stringHolders2.length; StringHolder[] stringHolders2Copy = new StringHolder[stringHolders2Length]; System.arraycopy(stringHolders2, 0, stringHolders2Copy, 0, stringHolders2Length); for (int x = stringHolders2Length; x-- > 0; ) { double maxScore = -2; int maxIndex = -1; for (int i = stringHolderPairsLength; i-- > 0; ) { InternalStringHolderPair stringHolderPair = stringHolderPairsCopy[i]; if (stringHolderPair == null) { continue; // skip to next slot } double stringHolderPairScore = stringHolderPair.maxScore(); if (stringHolderPairScore > maxScore) { maxScore = stringHolderPairScore; maxIndex = i; } } int stringHolder2Index = stringHolderPairsCopy[maxIndex].setStringHolder2(stringHolders2Copy); stringHolders2Copy[stringHolder2Index] = null; stringHolderPairsCopy[maxIndex] = null; // tell the remaining string holder pairs that one of the stringHolder2 entries has been assigned for (int i = stringHolderPairsLength; i-- > 0; ) { InternalStringHolderPair stringPair = stringHolderPairsCopy[i]; if (stringPair == null) { continue; // skip to next slot } stringPair.clearStringHolder2(stringHolder2Index); } } // the remaining string holder pairs do not get a stringHolder2 for (int x = stringHolderPairsLength; x-- > stringHolders2Length; ) { for (int i = stringHolderPairsLength; i-- > 0; ) { InternalStringHolderPair stringHolderPair = stringHolderPairsCopy[i]; if (stringHolderPair == null) { continue; // skip to next slot } stringHolderPair.setStringHolder2(null); } } } public String toString() { return StringTools.buildToStringFor(this, this.partialStringComparator); } // ********** member classes ********** /** * Extend the StringHolderPair interface with some behavior we * can delegate to the pairs themselves. */ private interface InternalStringHolderPair extends StringHolderPair { /** * Use the specified comparator to calculate the scores for * the pair's assigned string holder 1 with every possible * string holder 2. */ void calculateScores(PartialStringComparator psc, StringHolder[] stringHolders2); /** * Return the highest remaining score among those * calculated earlier. */ double maxScore(); /** * The engine has determined that the pair can now be assigned * a string holder 2 from the specified set of remaining string holders. */ int setStringHolder2(StringHolder[] stringHolders2); /** * The specified string holder 2 has been assigned to some other pair, * clear it and its score from the pair's state. */ void clearStringHolder2(int index); } /** * This string holder pair does not have a string holder 1. It is used when * there are more string holders in the second set of string holders * than in the first. */ private static class NullInternalStringHolderPair implements InternalStringHolderPair { private StringHolder stringHolder2; public StringHolder getStringHolder1() { return null; } public StringHolder getStringHolder2() { return this.stringHolder2; } public double getScore() { // return the lowest possible score return 0.0; } public void calculateScores(PartialStringComparator psc, StringHolder[] stringHolders2) { // do nothing } public double maxScore() { // return something less than the lowest possible score return -1; } public int setStringHolder2(StringHolder[] stringHolders2) { // all the remaining strings didn't match with any string; // so just take the first one for (int i = stringHolders2.length; i-- > 0; ) { if (stringHolders2[i] != null) { this.stringHolder2 = stringHolders2[i]; return i; } } throw new IllegalStateException("'stringHolders2' is empty"); } public void clearStringHolder2(int index) { // do nothing } public int compareTo(Object o) { return DEFAULT_COMPARATOR.compare(this, o); } public String toString() { StringBuffer sb = new StringBuffer(100); StringTools.buildSimpleToStringOn(this, sb); sb.append(" ("); sb.append("<null> vs. "); if (this.stringHolder2 == null) { sb.append("<null>"); } else { sb.append('"'); sb.append(this.stringHolder2.getString()); sb.append('"'); } sb.append(" => 0"); sb.append(')'); return sb.toString(); } } /** * Hold the pair of string holders and their score. Also hold some transient state * that is used by the engine while pairing up string holders. */ private static class SimpleInternalStringHolderPair implements InternalStringHolderPair { private final StringHolder stringHolder1; private StringHolder stringHolder2; private double score; private double[] scores; // transient private double maxScore; // transient private int maxScoreIndex; // transient SimpleInternalStringHolderPair(StringHolder stringHolder1, int stringHolders2Size) { super(); this.stringHolder1 = stringHolder1; this.score = -1; this.scores = new double[stringHolders2Size]; this.maxScore = -1; this.maxScoreIndex = -1; } public StringHolder getStringHolder1() { return this.stringHolder1; } public StringHolder getStringHolder2() { return this.stringHolder2; } public double getScore() { return this.score; } public void calculateScores(PartialStringComparator psc, StringHolder[] stringHolders2) { String localString1 = this.stringHolder1.getString(); double[] localScores = this.scores; double localMaxScore = this.maxScore; int localMaxScoreIndex = this.maxScoreIndex; for (int i = stringHolders2.length; i-- > 0; ) { double localScore = psc.compare(localString1, stringHolders2[i].getString()); localScores[i] = localScore; if (localScore > localMaxScore) { localMaxScore = localScore; localMaxScoreIndex = i; } } this.maxScore = localMaxScore; this.maxScoreIndex = localMaxScoreIndex; } public double maxScore() { if (this.maxScore == -1) { this.recalculateMaxScoreAndIndex(); } return this.maxScore; } private void recalculateMaxScoreAndIndex() { double[] localScores = this.scores; double localMaxScore = this.maxScore; int localMaxScoreIndex = this.maxScoreIndex; for (int i = localScores.length; i-- > 0; ) { double localScore = localScores[i]; if (localScore > localMaxScore) { localMaxScore = localScore; localMaxScoreIndex = i; } } this.maxScore = localMaxScore; this.maxScoreIndex = localMaxScoreIndex; } public int setStringHolder2(StringHolder[] stringHolders2) { int index = this.maxScoreIndex; if (stringHolders2 == null) { this.stringHolder2 = null; this.score = 0.0; } else { this.stringHolder2 = stringHolders2[index]; this.score = this.scores[index]; } this.scores = null; this.maxScore = 0; this.maxScoreIndex = -1; return index; } public void clearStringHolder2(int index) { this.scores[index] = -1; this.maxScore = -1; this.maxScoreIndex = -1; } public int compareTo(Object o) { return DEFAULT_COMPARATOR.compare(this, o); } public String toString() { return StringTools.buildToStringFor(this, this.additionalInfo()); } private String additionalInfo() { StringBuffer sb = new StringBuffer(200); sb.append("\""); sb.append(this.stringHolder1.getString()); sb.append("\" vs. "); if (this.stringHolder2 == null) { sb.append("<null>"); } else { sb.append('"'); sb.append(this.stringHolder2.getString()); sb.append('"'); } sb.append(" => "); sb.append(this.score); return sb.toString(); } } }