package ch.cyberduck.core; /* * @(#)OSXCollator.java * * Copyright (c) 2004-2007 Werner Randelshofer * Staldenmattweg 2, Immensee, CH-6405, Switzerland. * All rights reserved. * * The copyright of this software is owned by Werner Randelshofer. * You may not use, copy or modify this software, except in * accordance with the license agreement you entered into with * Werner Randelshofer. For details see accompanying license terms. */ import java.text.CollationKey; import java.text.Collator; import java.text.ParseException; import java.text.RuleBasedCollator; import java.util.Locale; /** * The OSXCollator strives to match the collation rules used by the Mac OS X * Finder and of Mac OS X file dialogs. * <p/> * If we wanted to match the OS X collation rules exactly, we would have to * implement the rules for all languages supported by Mac OS X and Java. * To reduce the amount of work needed for implementing these rules, the * OSXCollator changes the collation rules returned by * java.text.Collator.getInstance() to do the following: * <ul> * <li>Space characters are treated as primary collation differences</li> * <li>Hyphen characters are treated as primary collation differences</li> * <li>Sequence of digits (characters '0' through '9') is treated as a single * collation object. The current implementation supports sequences of up to * 999 characters length.</li> * </ul> * If java.text.Collator.getInstance() does not return an instance of * java.text.RuleBasedCollator, then the returned collator is used, and only * sequences of digits are changed to match the collation rules of Mac OS X. * * @author Werner Randelshofer * @version $Id: NaturalOrderCollator.java 5626 2009-12-19 23:30:40Z dkocher $ */ public class NaturalOrderCollator extends Collator { /** * @uml.property name="collator" */ private Collator collator; public NaturalOrderCollator() { this(Locale.getDefault()); } public NaturalOrderCollator(Locale locale) { collator = Collator.getInstance(locale); if(collator instanceof RuleBasedCollator) { String rules = ((RuleBasedCollator) collator).getRules(); // If hyphen is ignored except for tertiary difference, make it // a primary difference, and move in front of the first primary // difference found in the rules int pos = rules.indexOf(",'-'"); int primaryRelationPos = rules.indexOf('<'); if(primaryRelationPos == rules.indexOf("'<'")) { primaryRelationPos = rules.indexOf('<', primaryRelationPos + 2); } if(pos != -1 && pos < primaryRelationPos) { rules = rules.substring(0, pos) + rules.substring(pos + 4, primaryRelationPos) + "<'-'" + rules.substring(primaryRelationPos); } // If space is ignored except for secondary and tertiary // difference, make it a primary difference, and move in front // of the first primary difference found in the rules pos = rules.indexOf(";' '"); primaryRelationPos = rules.indexOf('<'); if(primaryRelationPos == rules.indexOf("'<'")) { primaryRelationPos = rules.indexOf('<', primaryRelationPos + 2); } if(pos != -1 && pos < primaryRelationPos) { rules = rules.substring(0, pos) + rules.substring(pos + 4, primaryRelationPos) + "<' '" + rules.substring(primaryRelationPos); } try { collator = new RuleBasedCollator(rules); } catch(ParseException e) { e.printStackTrace(); } } } @Override public int compare(String source, String target) { return collator.compare(expandNumbers(source), expandNumbers(target)); } @Override public CollationKey getCollationKey(String source) { return collator.getCollationKey(expandNumbers(source)); } @Override public boolean equals(Object o) { if(o instanceof NaturalOrderCollator) { NaturalOrderCollator that = (NaturalOrderCollator) o; return this.collator.equals(that.collator); } else { return false; } } public int hashCode() { return collator.hashCode(); } private String expandNumbers(String s) { if(s == null) { return null; } StringBuilder out = new StringBuilder(); StringBuilder digits = new StringBuilder(); for(int i = 0, n = s.length(); i < n; i++) { char ch = s.charAt(i); //if (Character.isDigit(ch)) { if(ch >= '0' && ch <= '9') { digits.append(ch); } else { if(digits.length() != 0) { if(digits.length() < 10) { out.append("00"); out.append(digits.length()); } else if(digits.length() < 100) { out.append("0"); out.append(digits.length()); } else if(digits.length() < 1000) { out.append(digits.length()); } else if(digits.length() > 999) { out.append("999"); } out.append(digits.toString()); digits.delete(0, digits.length()); } out.append(ch); } } if(digits.length() != 0) { if(digits.length() < 10) { out.append("00"); out.append(digits.length()); } else if(digits.length() < 100) { out.append("0"); out.append(digits.length()); } else if(digits.length() < 1000) { out.append(digits.length()); } else if(digits.length() > 999) { out.append("999"); } out.append(digits); } return out.toString(); } }