0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.openide.loaders;
0043:
0044: import java.beans.*;
0045: import java.io.IOException;
0046: import java.lang.ref.*;
0047: import java.util.*;
0048: import java.util.logging.*;
0049: import org.openide.filesystems.*;
0050: import org.openide.util.*;
0051:
0052: /** Watches a folder and its children.
0053: *
0054: * <p>{@link java.beans.PropertyChangeListener}s
0055: * may be registered which will be informed about changes in the ordered
0056: * children list. The {@link java.beans.PropertyChangeEvent}s fired by instances
0057: * of this class do neither contain information about the old value nor about
0058: * the new value of the children list.</p>
0059: *
0060: * <p>The list of children can be retrieved by calls to
0061: * the methods {@link #getChildren()} resp. {@link #getChildrenList()}. If you
0062: * want to filter the children which shall be included into the folder list,
0063: * call {@link #computeChildrenList(FolderListListener)}. The same is true
0064: * if you want to trigger children computation asynchronously. In this case
0065: * the implementation of {@link FolderListListener#finished(List)} shall be
0066: * used to get informed about the result of the computation.</p>
0067: *
0068: * <p>To retrieve the appropriate instance of this class for a given folder
0069: * call {@link #find(FileObject, boolean)}.</p>
0070: *
0071: * @author Jaroslav Tulach
0072: */
0073: final class FolderList extends Object implements FileChangeListener,
0074: DataObject.Container {
0075:
0076: /* -------------------------------------------------------------------- */
0077: /* -- Constants ------------------------------------------------------- */
0078: /* -------------------------------------------------------------------- */
0079:
0080: /** serial version UID */
0081: static final long serialVersionUID = -592616022226761148L;
0082:
0083: /** priority for tasks that can be run later */
0084: private static final int LATER_PRIORITY = Thread.NORM_PRIORITY;
0085:
0086: /** request processor for recognizing of folders */
0087: private static final RequestProcessor PROCESSOR = new RequestProcessor(
0088: "Folder recognizer" // NOI18N
0089: );
0090:
0091: /** map of (FileObject, Reference (FolderList)) */
0092: private static final Map<FileObject, Reference<FolderList>> map = new WeakHashMap<FileObject, Reference<FolderList>>(
0093: 101);
0094:
0095: /** refresh time in milliseconds */
0096: private static int REFRESH_TIME = -1; // will be updated in getRefreshTime
0097:
0098: /* -------------------------------------------------------------------- */
0099: /* -- Instance attributes --------------------------------------------- */
0100: /* -------------------------------------------------------------------- */
0101:
0102: /** data folder to work with */
0103: private FileObject folder;
0104:
0105: /** The task that computes the content of FolderList. There is also
0106: * only one computation task in the PROCESSOR for each FolderList.
0107: * Whenever a new change notification arrives (thru file listener)
0108: * the previous task is canceled (if not running) and new is created.
0109: */
0110: transient private RequestProcessor.Task refreshTask;
0111: /** task that is non-null if a setOrder has been called
0112: */
0113: transient private volatile RequestProcessor.Task comparatorTask;
0114:
0115: /** Primary files in this folder. Maps (FileObject, Reference (DataObject))
0116: */
0117: transient private Map<FileObject, Reference<DataObject>> primaryFiles = null;
0118:
0119: /** order of primary files (FileObject) */
0120: transient private List<FileObject> order;
0121:
0122: private static final Logger err = Logger
0123: .getLogger("org.openide.loaders.FolderList"); // NOI18N
0124:
0125: /** property change support */
0126: transient private PropertyChangeSupport pcs;
0127:
0128: /**
0129: * If true, this folder has been fully created (though it might
0130: * still be refreshing etc.). Used to avoid e.g. MDO.PROP_FILES
0131: * firing before the folder is ready.
0132: */
0133: transient private boolean folderCreated = false;
0134:
0135: transient private FileChangeListener weakFCL = FileUtil
0136: .weakFileChangeListener(this , null);
0137:
0138: /* -------------------------------------------------------------------- */
0139: /* -- Constructor (private) ------------------------------------------- */
0140: /* -------------------------------------------------------------------- */
0141:
0142: /**
0143: * @param df data folder to show
0144: */
0145: private FolderList(FileObject folder, boolean attach) {
0146: this .folder = folder;
0147: if (attach) {
0148: // creates object that handles all elements in array and
0149: // assignes it to the
0150: folder.addFileChangeListener(weakFCL);
0151: }
0152: }
0153:
0154: /* final void reassign(DataFolder df, FileObject fo) {
0155: folder = df;
0156: // reassign is called from DataFolder.handleMove()
0157: // in this time the folder - df - does not have
0158: // setup the right primary file
0159: // so the fo is the new primary file for df
0160: fo.addFileChangeListener (WeakListener.fileChange (this, fo));
0161: }
0162: */
0163: @Override
0164: public String toString() {
0165: return "FolderList{" + folder + "}"; // NOI18N
0166: }
0167:
0168: /* -------------------------------------------------------------------- */
0169: /* -- Factory method (static) ----------------------------------------- */
0170: /* -------------------------------------------------------------------- */
0171:
0172: /** A public method to get the correct list for given file object.
0173: *
0174: * @param folder the folder to find FolderList for
0175: * @param create if true than new FolderList should be created if it does not exists
0176: * @return the FolderList or null if create was false
0177: */
0178: public static FolderList find(FileObject folder, boolean create) {
0179: FolderList list = null;
0180: synchronized (FolderList.class) {
0181: Reference<FolderList> ref = map.get(folder);
0182: list = ref == null ? null : ref.get();
0183: if (list == null && create) {
0184: list = new FolderList(folder, true);
0185: map.put(folder, new SoftReference<FolderList>(list));
0186: }
0187: }
0188: return list;
0189: }
0190:
0191: /**
0192: * Has this FolderList finished creation of this list (at least once)?
0193: * @return true if it has been created (may still be refreshing), false if still in progress
0194: */
0195: public boolean isCreated() {
0196: return folderCreated;
0197: }
0198:
0199: /* -------------------------------------------------------------------- */
0200: /* -- Static methods -------------------------------------------------- */
0201: /* -------------------------------------------------------------------- */
0202:
0203: /** Checks whether the calling thread is the FolderRecognizer.
0204: */
0205: public static boolean isFolderRecognizerThread() {
0206: return PROCESSOR.isRequestProcessorThread();
0207: }
0208:
0209: /* -------------------------------------------------------------------- */
0210: /* -- Static methods to inform FolderList for a given folder ---------- */
0211: /* -------------------------------------------------------------------- */
0212:
0213: /** A method used to notify the FolderList system that order has changed
0214: * for a given file object.
0215: *
0216: * @param folder the affected file object
0217: */
0218: public static void changedFolderOrder(FileObject folder) {
0219: FolderList list = find(folder, false);
0220: if (list != null) {
0221: list.changeComparator();
0222: }
0223: }
0224:
0225: /** Called when a data system changed so much that there is a need for refresh
0226: * of a content of a folder.
0227: *
0228: * @param folder file object that can be affected
0229: */
0230: public static void changedDataSystem(FileObject folder) {
0231: FolderList list = find(folder, false);
0232: if (err.isLoggable(Level.FINE)) {
0233: err.fine("changedDataSystem: " + folder + " on "
0234: + Thread.currentThread()); // NOI18N
0235: }
0236: if (list != null) {
0237: list.refresh();
0238: }
0239: }
0240:
0241: /* -------------------------------------------------------------------- */
0242: /* -- Folder content and content processing --------------------------- */
0243: /* -------------------------------------------------------------------- */
0244:
0245: /** Computes array of children associated
0246: * with this folder.
0247: */
0248: public DataObject[] getChildren() {
0249: List<DataObject> res = getChildrenList();
0250: if (res == null) {
0251: return new DataObject[0];
0252: }
0253: DataObject[] arr = new DataObject[res.size()];
0254: res.toArray(arr);
0255: return arr;
0256: }
0257:
0258: /** List all children.
0259: * @return array with children
0260: */
0261: public List<DataObject> getChildrenList() {
0262: ListTask lt;
0263: try {
0264: DataObjectPool.getPOOL()
0265: .enterPrivilegedProcessor(PROCESSOR);
0266: lt = getChildrenList(null);
0267: lt.task.waitFinished();
0268: } finally {
0269: DataObjectPool.getPOOL().exitPrivilegedProcessor(PROCESSOR);
0270: }
0271: assert lt.result != null;
0272: return lt.result;
0273: }
0274:
0275: /** Blocks if the processing of content of folder is in progress.
0276: */
0277: public void waitProcessingFinished() {
0278: Task t = comparatorTask;
0279: if (t != null) {
0280: t.waitFinished();
0281: }
0282:
0283: t = refreshTask;
0284: if (t != null) {
0285: t.waitFinished();
0286: }
0287: }
0288:
0289: /** Starts computation of children list asynchronously.
0290: */
0291: public RequestProcessor.Task computeChildrenList(
0292: FolderListListener filter) {
0293: return getChildrenList(filter).task;
0294: }
0295:
0296: private ListTask getChildrenList(FolderListListener filter) {
0297: ListTask lt = new ListTask(filter);
0298: int priority = Thread.currentThread().getPriority();
0299:
0300: // and then post your read task and wait
0301: lt.task = PROCESSOR.post(lt, 0, priority);
0302: return lt;
0303: }
0304:
0305: /** Setter for sort mode.
0306: */
0307: private synchronized void changeComparator() {
0308: final boolean LOG = err.isLoggable(Level.FINE);
0309: if (LOG)
0310: err.fine("changeComparator on " + folder);
0311: final RequestProcessor.Task previous = comparatorTask;
0312: final RequestProcessor.Task[] COMP = new RequestProcessor.Task[1];
0313: synchronized (COMP) {
0314: comparatorTask = PROCESSOR.post(new Runnable() {
0315: public void run() {
0316: synchronized (COMP) {
0317: if (previous != null) {
0318: previous.waitFinished();
0319: }
0320: // if has been notified
0321: // change mode and regenerated children
0322: if (primaryFiles != null) {
0323: // the old children
0324: if (LOG)
0325: err.fine("changeComparator on "
0326: + folder + ": get old");
0327: List<DataObject> v = getObjects(null);
0328: if (v.size() != 0) {
0329: // the new children - also are stored to be returned next time from getChildrenList ()
0330: order = null;
0331: if (LOG)
0332: err
0333: .fine("changeComparator: get new");
0334: List<DataObject> r = getObjects(null);
0335: if (LOG)
0336: err
0337: .fine("changeComparator: fire change");
0338: fireChildrenChange(r, v);
0339: }
0340: }
0341: synchronized (FolderList.this ) {
0342: // clean the task if is my own not assigned by somebody else
0343: if (comparatorTask == COMP[0]) {
0344: comparatorTask = null;
0345: }
0346: }
0347: }
0348: }
0349: }, 0, Thread.MIN_PRIORITY);
0350: COMP[0] = comparatorTask;
0351: }
0352: }
0353:
0354: /* -------------------------------------------------------------------- */
0355: /* -- Refresh --------------------------------------------------------- */
0356: /* -------------------------------------------------------------------- */
0357:
0358: /** Refreshes the list of children.
0359: */
0360: public void refresh() {
0361: final long now = System.currentTimeMillis();
0362: final boolean LOG = err.isLoggable(Level.FINE);
0363: if (LOG)
0364: err.fine("refresh on " + folder + " @" + now);
0365: synchronized (this ) {
0366: if (refreshTask == null) {
0367: refreshTask = PROCESSOR.post(new Runnable() {
0368: public void run() {
0369: RequestProcessor.Task t = comparatorTask;
0370: if (t != null) {
0371: // first of all finish setting up comparator
0372: t.waitFinished();
0373: }
0374:
0375: if (LOG)
0376: err.fine("-- refresh on " + folder
0377: + ": now=" + now);
0378: if (primaryFiles != null) {
0379: // list of children is created, recreate it for new files
0380: createBoth(null, true);
0381: }
0382: }
0383: }, getRefreshTime(), LATER_PRIORITY);
0384: } else {
0385: refreshTask.schedule(getRefreshTime());
0386: }
0387: }
0388: }
0389:
0390: /** Tries to read the value of the refresh time from a system property.
0391: * If the system property is not present a default value (currently 10)
0392: * is used.
0393: */
0394: private static int getRefreshTime() {
0395: if (REFRESH_TIME >= 0) {
0396: return REFRESH_TIME;
0397: }
0398:
0399: String sysProp = System
0400: .getProperty("org.openide.loaders.FolderList.refresh.interval"); // NOI18N
0401: if (sysProp != null) {
0402: try {
0403: REFRESH_TIME = Integer.parseInt(sysProp);
0404: } catch (NumberFormatException nfe) {
0405: Logger.getLogger(FolderList.class.getName()).log(
0406: Level.WARNING, null, nfe);
0407: }
0408: }
0409: if (REFRESH_TIME < 0) {
0410: REFRESH_TIME = 10;
0411: }
0412: return REFRESH_TIME;
0413: }
0414:
0415: /* -------------------------------------------------------------------- */
0416: /* -- Implementation of FileChangeListener ---------------------------- */
0417: /* -------------------------------------------------------------------- */
0418:
0419: /** Fired when a file has been changed. Refreshes the list when a
0420: * has be changed which up to now was not a member of the list but
0421: * becomes a member as a consequence of the change.
0422: *
0423: * @param fe the event describing context where action has taken place
0424: */
0425: public void fileChanged(FileEvent fe) {
0426: final boolean LOG = err.isLoggable(Level.FINE);
0427: if (LOG)
0428: err.fine("fileChanged: " + fe);
0429:
0430: FileObject fo = fe.getFile();
0431:
0432: /** condition fo.isValid () is hot fix for solving problem (similar to #17328)
0433: * inside filesystems and should be reviewed.
0434: */
0435: if (fo.isData() && fo.isValid()) {
0436: // when a data on the disk has been changed, look whether we
0437: // should reparse children
0438: if (primaryFiles != null) {
0439: // a file has been changed and the list of files is created
0440: try {
0441: DataObject obj = DataObject.find(fo);
0442: if (!primaryFiles.containsKey(obj.getPrimaryFile())) {
0443: // BUGFIX: someone who recognized the file and who isn't registered
0444: // yet =>
0445: // may be still not O.K.
0446:
0447: // this primary file is not registered yet
0448: // so recreate list of children
0449: refresh();
0450: }
0451: } catch (DataObjectNotFoundException ex) {
0452: Logger.getLogger(FolderList.class.getName()).log(
0453: Level.WARNING, null, ex);
0454: // file without data object => no changes
0455: }
0456: }
0457:
0458: // Resort if sorting by last modification or size:
0459: DataFolder.SortMode sortMode = getComparator()
0460: .getSortMode();
0461: if (sortMode == DataFolder.SortMode.LAST_MODIFIED
0462: || sortMode == DataFolder.SortMode.SIZE) {
0463: changeComparator();
0464: }
0465: }
0466: }
0467:
0468: /** Fired when a file has been deleted.
0469: * @param fe the event describing context where action has taken place
0470: */
0471: public void fileDeleted(FileEvent fe) {
0472: final boolean LOG = err.isLoggable(Level.FINE);
0473: if (LOG)
0474: err.fine("fileDeleted: " + fe);
0475: // boolean debug = fe.getFile().toString().equals("P"); // NOI18N
0476: //if (debug) System.out.println ("fileDeleted: " + fe.getFile ()); // NOI18N
0477: //if (debug) System.out.println ("fileList: " + fileList + " file: " + fileList.get (fe.getFile ())); // NOI18N
0478: if (primaryFiles == null
0479: || primaryFiles.containsKey(fe.getFile())) {
0480: // one of main files has been deleted => reparse
0481: //if (debug) System.out.println ("RecreateChildenList"); // NOI18N
0482: refresh();
0483: //if (debug) System.out.println ("Done"); // NOI18N
0484: }
0485: }
0486:
0487: /** Fired when a new file has been created. This action can only be
0488: * listened in folders containing the created file up to the root of
0489: * file system.
0490: *
0491: * @param fe the event describing context where action has taken place
0492: */
0493: public void fileDataCreated(FileEvent fe) {
0494: final boolean LOG = err.isLoggable(Level.FINE);
0495: if (LOG)
0496: err.fine("fileDataCreated: " + fe);
0497: refresh();
0498: }
0499:
0500: /** Fired when a new file has been created. This action can only be
0501: * listened in folders containing the created file up to the root of
0502: * file system.
0503: *
0504: * @param fe the event describing context where action has taken place
0505: */
0506: public void fileFolderCreated(FileEvent fe) {
0507: final boolean LOG = err.isLoggable(Level.FINE);
0508: if (LOG)
0509: err.fine("fileFolderCreated: " + fe);
0510: refresh();
0511: }
0512:
0513: /** Fired when a new file has been renamed.
0514: *
0515: * @param fe the event describing context where action has taken place
0516: */
0517: public void fileRenamed(FileRenameEvent fe) {
0518: final boolean LOG = err.isLoggable(Level.FINE);
0519: if (LOG)
0520: err.fine("fileRenamed: " + fe);
0521: refresh();
0522: // Typically order may change as a result (#13820):
0523: changeComparator();
0524: }
0525:
0526: /** Fired when a file attribute has been changed.
0527: *
0528: * @param fe the event describing context where action has taken place
0529: */
0530: public void fileAttributeChanged(FileAttributeEvent fe) {
0531: final boolean LOG = err.isLoggable(Level.FINE);
0532: if (LOG)
0533: err.fine("fileAttributeChanged: " + fe);
0534: // update list when attrs defining order were changed
0535: if (fe.getFile() == folder) {
0536: /** Means one of attributes were changed*/
0537: if (fe.getName() == null) {
0538: changeComparator();
0539: return;
0540: }
0541: if (DataFolder.EA_ORDER.equals(fe.getName())
0542: || DataFolder.EA_SORT_MODE.equals(fe.getName())) {
0543: changeComparator();
0544: }
0545: } else if (FileUtil.affectsOrder(fe)) {
0546: changeComparator();
0547: }
0548: }
0549:
0550: /* -------------------------------------------------------------------- */
0551: /* -- Processing methods (only called in PROCESSOR) ------------------- */
0552: /* -------------------------------------------------------------------- */
0553:
0554: /** The comparator for this file objects.
0555: * @return the comparator to use
0556: */
0557: private FolderOrder getComparator() {
0558: return FolderOrder.findFor(folder);
0559: }
0560:
0561: /** Getter for list of children.
0562: * @param f filter to be notified about additions
0563: * @return List with DataObject types
0564: */
0565: private List<DataObject> getObjects(FolderListListener f) {
0566: final boolean LOG = err.isLoggable(Level.FINE);
0567: if (LOG)
0568: err.fine("getObjects on " + folder);
0569: List<DataObject> res;
0570: if (primaryFiles == null) {
0571: res = createBoth(f, false);
0572: } else {
0573: if (order != null) {
0574: res = createObjects(order, primaryFiles, f);
0575: } else {
0576: res = createObjects(primaryFiles.keySet(),
0577: primaryFiles, f);
0578: res = carefullySort(res, getComparator());
0579: order = createOrder(res);
0580: }
0581: }
0582: return res;
0583: /* createChildrenAndFiles ();/*
0584: ArrayList v = (Collection)childrenList.get ();
0585: //if (debug) System.out.println ("Children list xxxxxxxxxxxxxx");
0586: if (v == null) {
0587: //if (debug) System.out.println ("Create them xxxxxxxxxxxx");
0588: v = createChildrenList (f);
0589: //if (debug) System.out.println ("result: " + v);
0590: }
0591: return v;*/
0592: }
0593:
0594: /** Sort a list of DataObject's carefully.
0595: * The supplied comparator should supply a basic ordering,
0596: * and may also have an associated overriding partial ordering.
0597: * If the partial ordering is given and is self-contradictory,
0598: * it will be ignored and a warning issued.
0599: * @param l the list to sort
0600: * @param c a comparator and maybe partial comparator to use
0601: * @return the sorted list (may or may not be the same)
0602: */
0603: private List<DataObject> carefullySort(List<DataObject> l,
0604: FolderOrder c) {
0605: final boolean LOG = err.isLoggable(Level.FINE);
0606: if (LOG)
0607: err.fine("carefullySort on " + folder);
0608: Collections.sort(l, c);
0609: Map<FileObject, DataObject> files = new LinkedHashMap<FileObject, DataObject>(
0610: l.size());
0611: for (DataObject d : l) {
0612: FileObject f = d.getPrimaryFile();
0613: if (folder.equals(f.getParent())) {
0614: f.removeFileChangeListener(weakFCL);
0615: f.addFileChangeListener(weakFCL);
0616: files.put(f, d);
0617: }
0618: }
0619: if (LOG)
0620: err.fine("carefullySort before getOrder");
0621: List<FileObject> sorted = FileUtil.getOrder(files.keySet(),
0622: true);
0623: List<DataObject> dobs = new ArrayList<DataObject>(sorted.size());
0624: for (FileObject f : sorted) {
0625: dobs.add(files.get(f));
0626: }
0627: return dobs;
0628: }
0629:
0630: /** Creates list of primary files from the list of data objects.
0631: * @param list list of DataObject
0632: * @return list of FileObject
0633: */
0634: private static List<FileObject> createOrder(List<DataObject> list) {
0635: int size = list.size();
0636: List<FileObject> res = new ArrayList<FileObject>(size);
0637:
0638: for (int i = 0; i < size; i++) {
0639: res.add(list.get(i).getPrimaryFile());
0640: }
0641:
0642: return res;
0643: }
0644:
0645: /** Creates array of data objects from given order
0646: * and mapping between files and data objects.
0647: *
0648: * @param order list of FileObjects that define the order to use
0649: * @param map mapping (FileObject, Reference (DataObject)) to create data objects from
0650: * @param f filter that is notified about additions - only items
0651: * which are accepted by the filter will be added. Null means no filtering.
0652: * @return array of data objects
0653: */
0654: private/*static*/List<DataObject> createObjects(
0655: Collection<FileObject> order,
0656: Map<FileObject, Reference<DataObject>> map,
0657: FolderListListener f) {
0658: final boolean LOG = err.isLoggable(Level.FINE);
0659: if (LOG) {
0660: err.fine("createObjects on " + folder);
0661: }
0662: int size = order.size();
0663:
0664: Iterator it = order.iterator();
0665:
0666: List<DataObject> res = new ArrayList<DataObject>(size);
0667: for (FileObject fo : order) {
0668:
0669: if (LOG) {
0670: err.fine(" iterating" + fo);
0671: }
0672: if (!fo.isValid()) {
0673: if (LOG) {
0674: err.fine(" not valid, continue");
0675: }
0676: continue;
0677: }
0678: Reference<DataObject> ref = map.get(fo);
0679: DataObject obj = ref != null ? ref.get() : null;
0680:
0681: if (obj == null) {
0682: // try to find new data object
0683: if (LOG) {
0684: err.fine(" reference is " + ref + " obj is "
0685: + obj);
0686: }
0687: try {
0688: obj = DataObject.find(fo);
0689: ref = new SoftReference<DataObject>(obj);
0690: map.put(fo, ref);
0691: } catch (DataObjectNotFoundException ex) {
0692: Logger.getLogger(FolderList.class.getName()).log(
0693: Level.WARNING, null, ex);
0694: }
0695: }
0696: // add if accepted
0697: if (obj != null) {
0698: if (LOG) {
0699: err.fine(" deliver: ref is " + ref + " obj is "
0700: + obj);
0701: }
0702: // JST: Cannot be avoided otherwise DataObject.files () can be unconsistent
0703: // avoid to checkFiles(this)
0704: // obj.recognizedByFolder();
0705: if (f == null) {
0706: // accept all objects
0707: res.add(obj);
0708: } else {
0709: // allow the listener f to filter
0710: // objects in the array res
0711: f.process(obj, res);
0712: }
0713: }
0714: }
0715:
0716: if (f != null) {
0717: if (LOG) {
0718: err.fine(" finished: " + res); // NOI18N
0719: }
0720: f.finished(res);
0721: }
0722:
0723: if (LOG) {
0724: err.fine("createObjects ends on " + folder); // NOI18N
0725: }
0726: return res;
0727: }
0728:
0729: /** Scans for files in the folder and creates representation for
0730: * children. Fires info about changes in the nodes.
0731: *
0732: * @param filter listener to addition of nodes or null
0733: * @param notify true if changes in the children should be fired
0734: * @return vector of children
0735: */
0736: private List<DataObject> createBoth(FolderListListener filter,
0737: boolean notify) {
0738: final boolean LOG = err.isLoggable(Level.FINE);
0739: if (LOG)
0740: err.fine("createBoth on " + folder);
0741: // map for (FileObject, DataObject)
0742: final HashMap<FileObject, Reference<DataObject>> file = new HashMap<FileObject, Reference<DataObject>>();
0743:
0744: // list of all processed objects
0745: List<DataObject> all = new ArrayList<DataObject>();
0746: // result list to return from the method
0747: List<DataObject> res = new ArrayList<DataObject>();
0748:
0749: // map of current objects (FileObject, DataObject)
0750: final Map<FileObject, Reference<DataObject>> remove = primaryFiles == null ? new HashMap<FileObject, Reference<DataObject>>()
0751: : new HashMap<FileObject, Reference<DataObject>>(
0752: primaryFiles);
0753:
0754: // list of new objects to add
0755: final List<DataObject> add = new ArrayList<DataObject>();
0756:
0757: DataLoaderPool pool = DataLoaderPool.getDefault();
0758:
0759: // hashtable with FileObjects that are marked to be recognized
0760: // and that is why being out of enumeration
0761: final HashSet<FileObject> marked = new HashSet<FileObject>();
0762: DataLoader.RecognizedFiles recog = new DataLoader.RecognizedFiles() {
0763: /** Adds the file object to the marked hashtable.
0764: * @param fo file object (can be <CODE>null</CODE>)
0765: */
0766: public void markRecognized(FileObject fo) {
0767: if (fo != null) {
0768: marked.add(fo);
0769: }
0770: }
0771: };
0772: // enumeration of all files in the folder
0773: Enumeration<? extends FileObject> en = folder
0774: .getChildren(false);
0775: while (en.hasMoreElements()) {
0776: FileObject fo = en.nextElement();
0777: if (!marked.contains(fo)) {
0778: // the object fo has not been yet marked as recognized
0779: // => continue in computation
0780: DataObject obj;
0781: try {
0782: obj = pool.findDataObject(fo, recog);
0783: } catch (DataObjectExistsException ex) {
0784: // use existing data object
0785: obj = ex.getDataObject();
0786: } catch (IOException ex) {
0787: // data object not recognized or not found
0788: obj = null;
0789: Exceptions.printStackTrace(ex);
0790: } catch (Throwable td) {
0791: obj = null;
0792: err.log(Level.WARNING, "Error recognizing " + fo,
0793: td);
0794: }
0795:
0796: if (obj != null) {
0797: // adds object to data if it is not already there
0798:
0799: // avoid to checkFiles(this)
0800: obj.recognizedByFolder();
0801:
0802: // primary file
0803: FileObject primary = obj.getPrimaryFile();
0804:
0805: boolean doNotRemovePrimaryFile = false;
0806: if (!file.containsKey(primary)) {
0807: // realy added object, test if it is new
0808:
0809: // if we have not created primaryFiles before, then it is new
0810: boolean goIn = primaryFiles == null;
0811: if (!goIn) {
0812: Reference<DataObject> r = primaryFiles
0813: .get(primary);
0814: // if its primary file is not between original primary files
0815: // then data object is new
0816: goIn = r == null;
0817: if (!goIn) {
0818: // if the primary file is there, but the previous data object
0819: // exists and is different, then this one is new
0820: DataObject obj2 = r.get();
0821: goIn = obj2 == null || obj2 != obj;
0822: if (goIn) {
0823: doNotRemovePrimaryFile = true;
0824: }
0825: }
0826: }
0827:
0828: if (goIn) {
0829: // realy new
0830: add.add(obj);
0831: /* JST: In my opinion it should not be here
0832: * so I moved this out of this if. Is it ok?
0833:
0834: if (filter != null) {
0835: // fire info about addition
0836: filter.acceptDataObject (obj);
0837: }
0838: */
0839: }
0840: // adds the object
0841: all.add(obj);
0842: if (filter == null) {
0843: res.add(obj);
0844: } else {
0845: filter.process(obj, res);
0846: }
0847: }
0848:
0849: if (!doNotRemovePrimaryFile) {
0850: // this object exists it should not be removed
0851: remove.remove(primary);
0852: }
0853:
0854: // add it to the list of primary files
0855: file.put(primary,
0856: new SoftReference<DataObject>(obj));
0857: } else {
0858: // 1. nothing to add to data object list
0859: // 2. remove this object if it was in list of previous ones
0860: // 3. do not put the file into list of know primary files
0861: // => do nothing at all
0862: }
0863: }
0864: }
0865:
0866: // !!! section that fires info about changes should be here !!!
0867:
0868: // now file contains newly inserted files
0869: // data contains data objects
0870: // remove contains data objects that should be removed
0871: // add contains data object that were added
0872:
0873: primaryFiles = file;
0874:
0875: all = carefullySort(all, getComparator());
0876: order = createOrder(all);
0877: if (all.size() == res.size()) {
0878: // assume no filtering has been done
0879: res = all;
0880: } else {
0881: // sort also content of res
0882: res = carefullySort(res, getComparator());
0883: }
0884:
0885: ////if (debug) System.out.println ("Notified: " + notified + " added: " + add.size () + " removed: " + remove.size ()); // NOI18N
0886: if (notify) {
0887: fireChildrenChange(add, remove.keySet());
0888: }
0889:
0890: // notify the filter
0891: if (LOG) {
0892: err.fine("Notifying filter: " + filter); // NOI18N
0893: }
0894: if (filter != null) {
0895: filter.finished(res);
0896: }
0897:
0898: return res;
0899: }
0900:
0901: /* -------------------------------------------------------------------- */
0902: /* -- PropertyChangeListener management ------------------------------- */
0903: /* -------------------------------------------------------------------- */
0904:
0905: /** Fires info about change of children to the folder.
0906: * @param add added data objects
0907: * @param removed removed data objects
0908: */
0909: private void fireChildrenChange(Collection<?> add,
0910: Collection<?> removed) {
0911: if (pcs != null) {
0912: if (!add.isEmpty() || !removed.isEmpty()) {
0913: pcs.firePropertyChange(PROP_CHILDREN, null, null);
0914: }
0915: }
0916: }
0917:
0918: /** Removes property change listener.
0919: * @param l the listener
0920: */
0921: public void removePropertyChangeListener(PropertyChangeListener l) {
0922: if (pcs != null) {
0923: pcs.removePropertyChangeListener(l);
0924: }
0925: }
0926:
0927: /** Adds a listener.
0928: * @param l the listener
0929: */
0930: public synchronized void addPropertyChangeListener(
0931: PropertyChangeListener l) {
0932: if (pcs == null) {
0933: pcs = new PropertyChangeSupport(this );
0934: }
0935: pcs.addPropertyChangeListener(l);
0936: }
0937:
0938: /* -------------------------------------------------------------------- */
0939: /* -- Inner class ListTask -------------------------------------------- */
0940: /* -------------------------------------------------------------------- */
0941:
0942: /** Task that holds result and also task. Moreover
0943: * can do the computation.
0944: */
0945: private final class ListTask implements Runnable {
0946: private FolderListListener filter;
0947:
0948: public ListTask(FolderListListener filter) {
0949: this .filter = filter;
0950: }
0951:
0952: public List<DataObject> result;
0953: public RequestProcessor.Task task;
0954:
0955: public void run() {
0956: try {
0957: computeResult();
0958: } catch (Error t) {
0959: err.log(Level.WARNING,
0960: "cannot compute data objects for " + folder, t); // NOI18N
0961: throw t;
0962: } catch (RuntimeException ex) {
0963: err
0964: .log(Level.WARNING,
0965: "cannot compute data objects for "
0966: + folder, ex); // NOI18N
0967: throw ex;
0968: }
0969: }
0970:
0971: private void computeResult() {
0972: final boolean LOG = err.isLoggable(Level.FINE);
0973: if (LOG)
0974: err.fine("ListTask.run 1 on " + folder);
0975: // invokes the refresh task before we do anything else
0976: if (comparatorTask != null) {
0977: comparatorTask.waitFinished();
0978: }
0979: if (refreshTask != null) {
0980: refreshTask.waitFinished();
0981: }
0982: err.fine("ListTask.run 2");
0983:
0984: result = getObjects(filter);
0985: assert result != null;
0986: err.log(Level.FINE, "ListTask.run 3: {0}", result);
0987:
0988: folderCreated = true;
0989: }
0990:
0991: @Override
0992: public String toString() {
0993: return "ListTask@"
0994: + Integer
0995: .toHexString(System.identityHashCode(this ))
0996: + "[" + folder + "]"; // NOI18N
0997: }
0998: }
0999:
1000: }
|