/* $Id: PathComparator.java 17891 2010-01-12 21:21:06Z linus $
*****************************************************************************
* Copyright (c) 2009 Contributors - see below
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* tfmorris
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 2008 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
package org.argouml.uml.util;
import java.text.Collator;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.argouml.model.Model;
/**
* Comparator which orders model elements alphabetically by name, ignoring case.
* Ties are broken using names from the path in reverse order.
*
* @author Tom Morris <tfmorris@gmail.com>
*/
public class PathComparator implements Comparator {
private Collator collator;
/**
* Construct a PathComparator.
*/
public PathComparator() {
collator = Collator.getInstance();
collator.setStrength(Collator.PRIMARY);
}
/**
* Compare two UML elements names, ignoring case, using names from the path
* as tie breakers. As a convenience, we also compare simple strings using
* the same primary strength collator.
*
* @param o1 first model element
* @param o2 second model element
* @return -1, 0, 1
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Object o1, Object o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
}
return -1;
}
if (o2 == null) {
return 1;
}
if (o1.equals(o2)) {
return 0;
}
if (o1 instanceof String) {
if (o2 instanceof String) {
return collator.compare((String) o1, (String) o2);
} else if (Model.getFacade().isAUMLElement(o2)) {
// All strings collate before all UML elements
return -1;
}
}
if (o2 instanceof String && Model.getFacade().isAUMLElement(o1)) {
// All strings collate before all UML elements
return 1;
}
// Elements are collated first by name hoping for a quick solution
String name1, name2;
try {
name1 = Model.getFacade().getName(o1);
name2 = Model.getFacade().getName(o2);
} catch (IllegalArgumentException e) {
throw new ClassCastException(
"Model element or String required"
+ "\n - o1 = " + ((o1 == null) ? "(null)" : o1.toString())
+ "\n - o2 = " + ((o2 == null) ? "(null)" : o2.toString()));
}
if (name1 != null && name2 != null) {
int comparison = collator.compare(name1, name2);
if (comparison != 0) {
return comparison;
}
}
// and then by their enclosing path to fully distinguish them
return comparePaths(o1, o2);
}
/*
* Compare path of two elements in reverse order (inner to outer)
* using a primary strength text collator.
* This will collate e, E, �, � together, but not eliminate non-identical
* strings which collate in the same place.
*
* @return equivalent of list1.compareTo(list2)
*/
private int comparePaths(Object o1, Object o2) {
List<String> path1 =
Model.getModelManagementHelper().getPathList(o1);
Collections.reverse(path1);
List<String> path2 =
Model.getModelManagementHelper().getPathList(o2);
Collections.reverse(path2);
Iterator<String> i2 = path2.iterator();
Iterator<String> i1 = path1.iterator();
int caseSensitiveComparison = 0;
while (i2.hasNext()) {
String name2 = i2.next();
if (!i1.hasNext()) {
return -1;
}
String name1 = i1.next();
int comparison;
if (name1 == null) {
if (name2 == null) {
comparison = 0;
} else {
comparison = -1;
}
} else if (name2 == null) {
comparison = 1;
} else {
comparison = collator.compare(name1, name2);
}
if (comparison != 0) {
return comparison;
}
// Keep track of first non-equal comparison to use in case the
// case-insensitive comparisons all end up equal
if (caseSensitiveComparison == 0) {
if (name1 == null) {
if (name2 == null) {
caseSensitiveComparison = 0;
} else {
caseSensitiveComparison = -1;
}
} else if (name2 == null) {
caseSensitiveComparison = 1;
} else {
caseSensitiveComparison = name1.compareTo(name2);
}
}
}
if (i2.hasNext()) {
return 1;
}
// If the strings differed only in non-primary characteristics at
// some point (case, accent, etc) pick an arbitrary, but stable,
// collating order.
if (caseSensitiveComparison != 0) {
return caseSensitiveComparison;
}
// It's illegal in UML to have multiple elements in a namespace with
// the same name, but if it happens, keep them distinct so the user
// has a chance of catching the error. Pick an arbitrary, but stable,
// collating order.
// We don't call them equal because otherwise one will get eliminated
// from the TreeSet where this comparator is used.
return o1.toString().compareTo(o2.toString());
}
}