|
What this is
This file is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Other links
The source code
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.loaders;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.io.IOException;
import java.util.*;
import org.openide.filesystems.*;
import org.openide.loaders.DataFolder.SortMode;
/** A support for keeping order of children for folder list.
*
* @author Jaroslav Tulach
*/
final class FolderOrder extends Object implements Comparator {
/** Separator of names of two files. The first file should be before
* the second one in partial ordering
*/
private static final char SEP = '/';
/** a static map with (FileObject, Reference (Folder))
*/
private static final WeakHashMap map = new WeakHashMap (101);
/** A static of known folder orders. Though we hold the
* FolderOrder with a soft reference which can be collected, even
* if this happens we would like the new FolderOrder to have any
* previously determined order attribute. Otherwise under obscure
* circumstances (#15381) it is possible for the IDE to go into an
* endless loop recalculating folder orders, since they keep
* getting collected.
*/
private static final Map knownOrders = Collections.synchronizedMap(new WeakHashMap(50)); // Map
/** map of names of primary files of objects to their index or null */
private Map order; // Map
/** file to store data in */
private FileObject folder;
/** if true, partial orderings on disk should be ignored for files in the order */
private boolean ignorePartials;
/** a reference to sort mode of this folder order */
private SortMode sortMode;
/** previous value of the order */
private Object previous;
/** Constructor.
* @param folder the folder to create order for
*/
private FolderOrder (FileObject folder) {
this.folder = folder;
}
/** Changes a sort order for this order
* @param mode sort mode.
*/
public void setSortMode (SortMode mode) throws IOException {
// store the mode to properties
sortMode = mode;
mode.write (folder); // writes attribute EA_SORT_MODE -> updates FolderList
// FolderList.changedFolderOrder (folder);
}
/** Getter for the sort order.
*/
public SortMode getSortMode () {
if (sortMode == null) {
sortMode = SortMode.read (folder);
}
return sortMode;
}
/** Changes the order of data objects.
*/
public synchronized void setOrder (DataObject[] arr) throws IOException {
if (arr != null) {
order = new HashMap (arr.length * 4 / 3 + 1);
// each object only once
Enumeration en = org.openide.util.Enumerations.removeDuplicates (
org.openide.util.Enumerations.array (arr)
);
int i = 0;
while (en.hasMoreElements ()) {
DataObject obj = (DataObject)en.nextElement ();
FileObject fo = obj.getPrimaryFile ();
if (folder.equals (fo.getParent ())) {
// object for my folder
order.put (fo.getNameExt (), new Integer (i++));
}
}
// Explicit order has been set, if written please clear affected
// order markings.
ignorePartials = true;
} else {
order = null;
}
write (); // writes attribute EA_ORDER -> updates FolderList
// FolderList.changedFolderOrder (folder);
}
/**
* Get ordering constraints for this folder.
* Returns a map from data objects to lists of data objects they should precede.
* @param objects a collection of data objects known to be in the folder
* @return a constraint map, or null if there are no constraints
*/
public synchronized Map getOrderingConstraints(Collection objects) {
final Set partials = readPartials ();
if (partials.isEmpty ()) {
return null;
} else {
Map objectsByName = new HashMap();
Iterator it = objects.iterator();
while (it.hasNext()) {
DataObject d = (DataObject)it.next();
objectsByName.put(d.getPrimaryFile().getNameExt(), d);
}
Map m = new HashMap();
it = partials.iterator();
while (it.hasNext()) {
String constraint = (String)it.next();
int idx = constraint.indexOf(SEP);
String a = constraint.substring(0, idx);
String b = constraint.substring(idx + 1);
if (ignorePartials && (order.containsKey(a) || order.containsKey(b))) {
continue;
}
DataObject ad = (DataObject)objectsByName.get(a);
if (ad == null) {
continue;
}
DataObject bd = (DataObject)objectsByName.get(b);
if (bd == null) {
continue;
}
List l = (List)m.get(ad);
if (l == null) {
m.put(ad, l = new LinkedList());
}
l.add(bd);
}
return m;
}
}
/** Read the list of intended partial orders from disk.
* Each element is a string of the form "a
Enumeration e = folder.getAttributes ();
Set s = new HashSet ();
while (e.hasMoreElements ()) {
String name = (String) e.nextElement ();
if (name.indexOf (SEP) != -1) {
Object value = folder.getAttribute (name);
if ((value instanceof Boolean) && ((Boolean) value).booleanValue ())
s.add (name);
}
}
return s;
}
/** Compares two data object or two nodes.
*/
public int compare (Object o1, Object o2) {
DataObject obj1 = (DataObject) o1;
DataObject obj2 = (DataObject) o2;
Integer i1 = (order == null) ? null : (Integer)order.get (obj1.getPrimaryFile ().getNameExt ());
Integer i2 = (order == null) ? null : (Integer)order.get (obj2.getPrimaryFile ().getNameExt ());
if (i1 == null) {
if (i2 != null) return 1;
// compare by the provided comparator
return getSortMode ().compare (obj1, obj2);
} else {
if (i2 == null) return -1;
// compare integers
if (i1.intValue () == i2.intValue ()) return 0;
if (i1.intValue () < i2.intValue ()) return -1;
return 1;
}
}
/** Stores the order to files.
*/
public void write () throws IOException {
// Let it throw the IOException:
//if (folder.getFileSystem ().isReadOnly ()) return; // cannot write to read-only FS
if (order == null) {
// if we should clear the order
folder.setAttribute (DataFolder.EA_ORDER, null);
} else {
// Stores list of file names separated by /
java.util.Iterator it = order.entrySet ().iterator ();
String[] filenames = new String[order.size ()];
while (it.hasNext ()) {
Map.Entry en = (Map.Entry)it.next ();
String fo = (String)en.getKey ();
int indx = ((Integer)en.getValue ()).intValue ();
filenames[indx] = fo;
}
StringBuffer buf = new StringBuffer (255);
for (int i = 0; i < filenames.length; i++) {
if (i > 0) {
buf.append ('/');
}
buf.append (filenames[i]);
}
folder.setAttribute (DataFolder.EA_ORDER, buf.toString ());
if (ignorePartials) {
// Reverse any existing partial orders among files explicitly
// mentioned in the order.
Set p = readPartials ();
if (! p.isEmpty ()) {
Set f = new HashSet (); // Set for filenames
it = order.keySet ().iterator ();
while (it.hasNext ()) {
String fo = (String) it.next ();
f.add (fo);
}
it = p.iterator ();
while (it.hasNext ()) {
String s = (String) it.next ();
int idx = s.indexOf (SEP);
if (f.contains (s.substring (0, idx)) &&
f.contains (s.substring (idx + 1))) {
folder.setAttribute (s, null);
}
}
}
// Need not do this again for this order:
ignorePartials = false;
}
}
}
/** Reads the order from disk.
*/
private void read () {
Object o = folder.getAttribute (DataFolder.EA_ORDER);
if ((previous == null && o == null) ||
(previous != null && previous.equals (o))) {
// no change in order
return;
}
if ((o instanceof Object[]) && (previous instanceof Object[])) {
if (compare((Object[]) o, (Object[]) previous)) {
return;
}
}
doRead (o);
previous = o;
if (previous != null) {
knownOrders.put(folder, previous);
}
FolderList.changedFolderOrder (folder);
}
/** Compares two arrays */
private static boolean compare(Object[] a, Object[] b) {
if (a == b) {
return true;
}
int len = Math.min(a.length, b.length);
for (int i = 0; i < len; i++) {
if (a[i] != b[i]) {
if (a[i] == null) {
return false;
}
if (a[i].equals(b[i])) {
continue;
}
if ((a[i] instanceof Object[]) && (b[i] instanceof Object[])) {
if (compare((Object[]) a[i], (Object[]) b[i])) {
continue;
} else {
return false;
}
} else {
return false;
}
}
}
Object[] arr = (a.length > b.length) ? a : b;
if (checkNonNull(arr, len)) {
return false;
}
return true;
}
private static boolean checkNonNull(Object[] a, int from) {
for (int i = from; i < a.length; i++) {
if (a[i] != null) {
return true;
}
}
return false;
}
/** Reads the values from the object o
* @param o value of attribute EA_ORDER
*/
private void doRead (Object o) {
if (o == null) {
order = null;
return;
} else if (o instanceof String[][]) {
// Compatibility:
String[][] namesExts = (String[][]) o;
if (namesExts.length != 2) {
order = null;
return;
}
String[] names = namesExts[0];
String[] exts = namesExts[1];
if (names == null || exts == null || names.length != exts.length) {
// empty order
order = null;
return;
}
HashMap set = new HashMap (names.length);
for (int i = 0; i < names.length; i++) {
set.put (names[i], new Integer (i));
}
order = set;
return;
} else if (o instanceof String) {
// Current format:
String sepnames = (String) o;
HashMap set = new HashMap ();
StringTokenizer tok = new StringTokenizer (sepnames, "/"); // NOI18N
int i = 0;
while (tok.hasMoreTokens ()) {
String file = tok.nextToken ();
set.put (file, new Integer (i));
i++;
}
order = set;
return;
} else {
// Unknown format:
order = null;
return;
}
}
/** Creates order for given folder object.
* @param f the folder
* @return the order
*/
public static FolderOrder findFor (FileObject folder) {
FolderOrder order = null;
synchronized (map) {
Reference ref = (Reference)map.get (folder);
order = ref == null ? null : (FolderOrder)ref.get ();
if (order == null) {
order = new FolderOrder (folder);
order.previous = knownOrders.get(folder);
order.doRead(order.previous);
map.put (folder, new SoftReference (order));
}
}
// always reread the order from disk, so it is uptodate
synchronized (order) {
order.read ();
return order;
}
}
}
|