001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.loaders;
043:
044: import java.util.*;
045:
046: import org.openide.filesystems.FileObject;
047:
048: /** This class is a lazy initialized set of FileObjects representing entries
049: * in MultiDataObject class. Primary file object is returned as the first from
050: * this set, the secondary fileobjects are sorted by <code>getNameExt()</code>
051: * method result alphabetically.
052: * <p>
053: * This class is an implementation of performance enhancement #16396.
054: *
055: * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=16396">Issue #16396</a>
056: *
057: * @author Petr Hamernik
058: */
059: final class FilesSet implements Set<FileObject> {
060:
061: /** MDO which created this set */
062: private final MultiDataObject mymdo;
063:
064: /** A flag */
065: private boolean lazyWorkDone;
066:
067: /** Primary file. It is returned first. */
068: private FileObject primaryFile;
069:
070: /** The link to secondary variable of MultiDataObject. Reading of the content
071: * must be synchronized on this map.
072: */
073: private Map<FileObject, MultiDataObject.Entry> secondary;
074:
075: /** The set containing all files. It is <code>null</code> and is lazy initialized
076: * when necessary.
077: */
078: private TreeSet<FileObject> delegate;
079:
080: /** Creates a new instance of FilesSet for the MultiDataObject.
081: * @param primaryFile The primary file - this object is returned first.
082: * @param secondary the map of secondary file objects. It is used
083: * for initialization <code>delegate</code> variable when necessary.
084: */
085: public FilesSet(MultiDataObject mdo) {
086: this .mymdo = mdo;
087: }
088:
089: /** Does the work which was originally done in MDO.files() method. */
090: private void doLazyWork() {
091: synchronized (this ) {
092: if (!lazyWorkDone) {
093: lazyWorkDone = true;
094:
095: synchronized (mymdo.synchObjectSecondary()) {
096: // cleans up invalid entries
097: mymdo.secondaryEntries();
098: primaryFile = mymdo.getPrimaryFile();
099: secondary = mymdo.getSecondary();
100: }
101: }
102: }
103: }
104:
105: /** Perform lazy initialization of delegate TreeSet.
106: */
107: private Set<FileObject> getDelegate() {
108: doLazyWork();
109: // This synchronized block was moved from MultiDataObject.files() method,
110: // because of lazy initialization of delegate TreeSet.
111: // Hopefully won't cause threading problems.
112: synchronized (secondary) {
113: if (delegate == null) {
114: delegate = new TreeSet<FileObject>(
115: new FilesComparator());
116: delegate.add(primaryFile);
117: delegate.addAll(secondary.keySet());
118: }
119: }
120: return delegate;
121: }
122:
123: // =====================================================================
124: // Implementation of Set interface methods
125: // =====================================================================
126:
127: public boolean add(FileObject obj) {
128: return getDelegate().add(obj);
129: }
130:
131: public boolean addAll(Collection<? extends FileObject> collection) {
132: return getDelegate().addAll(collection);
133: }
134:
135: public void clear() {
136: getDelegate().clear();
137: }
138:
139: public boolean contains(Object obj) {
140: return getDelegate().contains(obj);
141: }
142:
143: public boolean containsAll(Collection<?> collection) {
144: return getDelegate().containsAll(collection);
145: }
146:
147: public boolean isEmpty() {
148: doLazyWork();
149: synchronized (secondary) {
150: return (delegate == null) ? false : delegate.isEmpty();
151: }
152: }
153:
154: public Iterator<FileObject> iterator() {
155: doLazyWork();
156: synchronized (secondary) {
157: return (delegate == null) ? new FilesIterator() : delegate
158: .iterator();
159: }
160: }
161:
162: public boolean remove(Object obj) {
163: return getDelegate().remove(obj);
164: }
165:
166: public boolean removeAll(Collection<?> collection) {
167: return getDelegate().removeAll(collection);
168: }
169:
170: public boolean retainAll(Collection<?> collection) {
171: return getDelegate().retainAll(collection);
172: }
173:
174: public int size() {
175: doLazyWork();
176: synchronized (secondary) {
177: return (delegate == null) ? (secondary.size() + 1)
178: : delegate.size();
179: }
180: }
181:
182: public Object[] toArray() {
183: return getDelegate().toArray();
184: }
185:
186: public <T> T[] toArray(T[] obj) {
187: return getDelegate().toArray(obj);
188: }
189:
190: public boolean equals(Object obj) {
191: return getDelegate().equals(obj);
192: }
193:
194: public String toString() {
195: return getDelegate().toString();
196: }
197:
198: public int hashCode() {
199: return getDelegate().hashCode();
200: }
201:
202: /** Iterator for FilesSet. It returns the primaryFile first and
203: * then initialize the delegate iterator for secondary files.
204: */
205: private final class FilesIterator implements Iterator<FileObject> {
206: /** Was the first element (primary file) already returned?
207: */
208: private boolean first = true;
209:
210: /** Delegation iterator for secondary files. It is lazy initialized after
211: * the first element is returned.
212: */
213: private Iterator<FileObject> itDelegate = null;
214:
215: FilesIterator() {
216: }
217:
218: public boolean hasNext() {
219: return first ? true : getIteratorDelegate().hasNext();
220: }
221:
222: public FileObject next() {
223: if (first) {
224: first = false;
225: return FilesSet.this .primaryFile;
226: } else {
227: return getIteratorDelegate().next();
228: }
229: }
230:
231: public void remove() {
232: getIteratorDelegate().remove();
233: }
234:
235: /** Initialize the delegation iterator.
236: */
237: private Iterator<FileObject> getIteratorDelegate() {
238: if (itDelegate == null) {
239: // this should return iterator of all files of the MultiDataObject...
240: itDelegate = FilesSet.this .getDelegate().iterator();
241: // ..., so it is necessary to skip the primary file
242: itDelegate.next();
243: }
244: return itDelegate;
245: }
246: }
247:
248: /** Comparator for file objects. The primary file is less than any other file,
249: * so it is returned first. Other files are compared by getNameExt() method
250: * result.
251: */
252: private final class FilesComparator implements
253: Comparator<FileObject> {
254: FilesComparator() {
255: }
256:
257: public int compare(FileObject f1, FileObject f2) {
258: if (f1 == f2)
259: return 0;
260:
261: if (f1 == primaryFile)
262: return -1;
263:
264: if (f2 == primaryFile)
265: return 1;
266:
267: int res = f1.getNameExt().compareTo(f2.getNameExt());
268:
269: if (res == 0) {
270: // check whether they both live on the same fs
271: try {
272: if (f1.getFileSystem() == f2.getFileSystem()) {
273: return 0;
274: }
275: // different fs --> compare the fs names
276: return f1.getFileSystem().getSystemName()
277: .compareTo(
278: f2.getFileSystem().getSystemName());
279: } catch (org.openide.filesystems.FileStateInvalidException fsie) {
280: // should not happen - but the names were the same
281: // so we declare they are the same (even if the filesystems
282: // crashed meanwhile)
283: return 0;
284: }
285: }
286:
287: return res;
288: }
289: }
290: }
|