gt:                 if (remSize > 0 && addSize == 0) {
context: /*
   * Copyright (c) 1998, 2009, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package javax.swing.plaf.basic;
  
  import java.io.File;
  import java.util.*;
  import java.util.concurrent.Callable;
  import javax.swing.*;
  import javax.swing.filechooser.*;
  import javax.swing.event.*;
  import java.beans.*;
  
  import sun.awt.shell.ShellFolder;
  
  /**
   * Basic implementation of a file list.
   *
   * @author Jeff Dinkins
   */
  public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener {
  
      private JFileChooser filechooser = null;
      // PENDING(jeff) pick the size more sensibly
      private Vector<File> fileCache = new Vector<File>(50);
      private LoadFilesThread loadThread = null;
      private Vector<File> files = null;
      private Vector<File> directories = null;
      private int fetchID = 0;
  
      private PropertyChangeSupport changeSupport;
  
      private boolean busy = false;
  
      public BasicDirectoryModel(JFileChooser filechooser) {
          this.filechooser = filechooser;
          validateFileCache();
      }
  
      public void propertyChange(PropertyChangeEvent e) {
          String prop = e.getPropertyName();
          if(prop == JFileChooser.DIRECTORY_CHANGED_PROPERTY ||
             prop == JFileChooser.FILE_VIEW_CHANGED_PROPERTY ||
             prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY ||
             prop == JFileChooser.FILE_HIDING_CHANGED_PROPERTY ||
             prop == JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) {
              validateFileCache();
          } else if ("UI".equals(prop)) {
              Object old = e.getOldValue();
              if (old instanceof BasicFileChooserUI) {
                  BasicFileChooserUI ui = (BasicFileChooserUI) old;
                  BasicDirectoryModel model = ui.getModel();
                  if (model != null) {
                      model.invalidateFileCache();
                  }
              }
          } else if ("JFileChooserDialogIsClosingProperty".equals(prop)) {
              invalidateFileCache();
          }
      }
  
      /**
       * This method is used to interrupt file loading thread.
       */
      public void invalidateFileCache() {
          if (loadThread != null) {
              loadThread.interrupt();
              loadThread.cancelRunnables();
              loadThread = null;
          }
      }
  
      public Vector<File> getDirectories() {
          synchronized(fileCache) {
              if (directories != null) {
                  return directories;
              }
              Vector fls = getFiles();
              return directories;
          }
      }
  
      public Vector<File> getFiles() {
          synchronized(fileCache) {
              if (files != null) {
                  return files;
              }
              files = new Vector<File>();
              directories = new Vector<File>();
              directories.addElement(filechooser.getFileSystemView().createFileObject(
                  filechooser.getCurrentDirectory(), "..")
              );
  
              for (int i = 0; i < getSize(); i++) {
                  File f = fileCache.get(i);
                  if (filechooser.isTraversable(f)) {
                      directories.add(f);
                  } else {
                      files.add(f);
                  }
              }
              return files;
          }
      }
  
      public void validateFileCache() {
          File currentDirectory = filechooser.getCurrentDirectory();
          if (currentDirectory == null) {
              return;
          }
          if (loadThread != null) {
              loadThread.interrupt();
              loadThread.cancelRunnables();
          }
  
          setBusy(true, ++fetchID);
  
          loadThread = new LoadFilesThread(currentDirectory, fetchID);
          loadThread.start();
      }
  
      /**
       * Renames a file in the underlying file system.
       *
       * @param oldFile a <code>File</code> object representing
       *        the existing file
       * @param newFile a <code>File</code> object representing
       *        the desired new file name
       * @return <code>true</code> if rename succeeded,
       *        otherwise <code>false</code>
       * @since 1.4
       */
      public boolean renameFile(File oldFile, File newFile) {
          synchronized(fileCache) {
              if (oldFile.renameTo(newFile)) {
                  validateFileCache();
                  return true;
              }
              return false;
          }
      }
  
  
      public void fireContentsChanged() {
          // System.out.println("BasicDirectoryModel: firecontentschanged");
          fireContentsChanged(this, 0, getSize()-1);
      }
  
      public int getSize() {
          return fileCache.size();
      }
  
      public boolean contains(Object o) {
          return fileCache.contains(o);
      }
  
      public int indexOf(Object o) {
          return fileCache.indexOf(o);
      }
  
      public Object getElementAt(int index) {
          return fileCache.get(index);
      }
  
      /**
       * Obsolete - not used.
       */
      public void intervalAdded(ListDataEvent e) {
      }
  
      /**
       * Obsolete - not used.
       */
      public void intervalRemoved(ListDataEvent e) {
      }
  
      protected void sort(Vector<? extends File> v){
          ShellFolder.sort(v);
      }
  
      // Obsolete - not used
      protected boolean lt(File a, File b) {
          // First ignore case when comparing
          int diff = a.getName().toLowerCase().compareTo(b.getName().toLowerCase());
          if (diff != 0) {
              return diff < 0;
          } else {
              // May differ in case (e.g. "mail" vs. "Mail")
              return a.getName().compareTo(b.getName()) < 0;
          }
      }
  
  
      class LoadFilesThread extends Thread {
          File currentDirectory = null;
          int fid;
          Vector<DoChangeContents> runnables = new Vector<DoChangeContents>(10);
  
          public LoadFilesThread(File currentDirectory, int fid) {
              super("Basic L&F File Loading Thread");
              this.currentDirectory = currentDirectory;
              this.fid = fid;
          }
  
          public void run() {
              run0();
              setBusy(false, fid);
          }
  
          public void run0() {
              FileSystemView fileSystem = filechooser.getFileSystemView();
  
              if (isInterrupted()) {
                  return;
              }
  
              File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
  
              if (isInterrupted()) {
                  return;
              }
  
              final Vector<File> newFileCache = new Vector<File>();
              Vector<File> newFiles = new Vector<File>();
  
              // run through the file list, add directories and selectable files to fileCache
              // Note that this block must be OUTSIDE of Invoker thread because of
              // deadlock possibility with custom synchronized FileSystemView
              for (File file : list) {
                  if (filechooser.accept(file)) {
                      boolean isTraversable = filechooser.isTraversable(file);
  
                      if (isTraversable) {
                          newFileCache.addElement(file);
                      } else if (filechooser.isFileSelectionEnabled()) {
                          newFiles.addElement(file);
                      }
  
                      if (isInterrupted()) {
                          return;
                      }
                  }
              }
  
              // First sort alphabetically by filename
              sort(newFileCache);
              sort(newFiles);
  
              newFileCache.addAll(newFiles);
  
              // To avoid loads of synchronizations with Invoker and improve performance we
              // execute the whole block on the COM thread
              DoChangeContents doChangeContents = ShellFolder.invoke(new Callable<DoChangeContents>() {
                  public DoChangeContents call() {
                      int newSize = newFileCache.size();
                      int oldSize = fileCache.size();
  
                      if (newSize > oldSize) {
                          //see if interval is added
                          int start = oldSize;
                          int end = newSize;
                          for (int i = 0; i < oldSize; i++) {
                              if (!newFileCache.get(i).equals(fileCache.get(i))) {
                                  start = i;
                                  for (int j = i; j < newSize; j++) {
                                      if (newFileCache.get(j).equals(fileCache.get(i))) {
                                          end = j;
                                          break;
                                      }
                                  }
                                  break;
                              }
                          }
                          if (start >= 0 && end > start
                              && newFileCache.subList(end, newSize).equals(fileCache.subList(start, oldSize))) {
                              if (isInterrupted()) {
                                  return null;
                              }
                              return new DoChangeContents(newFileCache.subList(start, end), start, null, 0, fid);
                          }
                      } else if (newSize < oldSize) {
                          //see if interval is removed
                          int start = -1;
                          int end = -1;
                          for (int i = 0; i < newSize; i++) {
                              if (!newFileCache.get(i).equals(fileCache.get(i))) {
                                  start = i;
                                  end = i + oldSize - newSize;
                                  break;
                              }
                          }
                          if (start >= 0 && end > start
                              && fileCache.subList(end, oldSize).equals(newFileCache.subList(start, newSize))) {
                              if (isInterrupted()) {
                                  return null;
                              }
                              return new DoChangeContents(null, 0, new Vector(fileCache.subList(start, end)), start, fid);
                          }
                      }
                      if (!fileCache.equals(newFileCache)) {
                          if (isInterrupted()) {
                              cancelRunnables(runnables);
                          }
                          return new DoChangeContents(newFileCache, 0, fileCache, 0, fid);
                      }
                      return null;
                  }
              });
  
              if (doChangeContents != null) {
                  runnables.addElement(doChangeContents);
                  SwingUtilities.invokeLater(doChangeContents);
              }
          }
  
  
          public void cancelRunnables(Vector<DoChangeContents> runnables) {
              for (DoChangeContents runnable : runnables) {
                  runnable.cancel();
              }
          }
  
          public void cancelRunnables() {
              cancelRunnables(runnables);
          }
     }
  
  
      /**
       * Adds a PropertyChangeListener to the listener list. The listener is
       * registered for all bound properties of this class.
       * <p>
       * If <code>listener</code> is <code>null</code>,
       * no exception is thrown and no action is performed.
       *
       * @param    listener  the property change listener to be added
       *
       * @see #removePropertyChangeListener
       * @see #getPropertyChangeListeners
       *
       * @since 1.6
       */
      public void addPropertyChangeListener(PropertyChangeListener listener) {
          if (changeSupport == null) {
              changeSupport = new PropertyChangeSupport(this);
          }
          changeSupport.addPropertyChangeListener(listener);
      }
  
      /**
       * Removes a PropertyChangeListener from the listener list.
       * <p>
       * If listener is null, no exception is thrown and no action is performed.
       *
       * @param listener the PropertyChangeListener to be removed
       *
       * @see #addPropertyChangeListener
       * @see #getPropertyChangeListeners
       *
       * @since 1.6
       */
      public void removePropertyChangeListener(PropertyChangeListener listener) {
          if (changeSupport != null) {
              changeSupport.removePropertyChangeListener(listener);
          }
      }
  
      /**
       * Returns an array of all the property change listeners
       * registered on this component.
       *
       * @return all of this component's <code>PropertyChangeListener</code>s
       *         or an empty array if no property change
       *         listeners are currently registered
       *
       * @see      #addPropertyChangeListener
       * @see      #removePropertyChangeListener
       * @see      java.beans.PropertyChangeSupport#getPropertyChangeListeners
       *
       * @since 1.6
       */
      public PropertyChangeListener[] getPropertyChangeListeners() {
          if (changeSupport == null) {
              return new PropertyChangeListener[0];
          }
          return changeSupport.getPropertyChangeListeners();
      }
  
      /**
       * Support for reporting bound property changes for boolean properties.
       * This method can be called when a bound property has changed and it will
       * send the appropriate PropertyChangeEvent to any registered
       * PropertyChangeListeners.
       *
       * @param propertyName the property whose value has changed
       * @param oldValue the property's previous value
       * @param newValue the property's new value
       *
       * @since 1.6
       */
      protected void firePropertyChange(String propertyName,
                                        Object oldValue, Object newValue) {
          if (changeSupport != null) {
              changeSupport.firePropertyChange(propertyName,
                                               oldValue, newValue);
          }
      }
  
  
      /**
       * Set the busy state for the model. The model is considered
       * busy when it is running a separate (interruptable)
       * thread in order to load the contents of a directory.
       */
      private synchronized void setBusy(final boolean busy, int fid) {
          if (fid == fetchID) {
              boolean oldValue = this.busy;
              this.busy = busy;
  
              if (changeSupport != null && busy != oldValue) {
                  SwingUtilities.invokeLater(new Runnable() {
                      public void run() {
                          firePropertyChange("busy", !busy, busy);
                      }
                  });
              }
          }
      }
  
  
      class DoChangeContents implements Runnable {
          private List<File> addFiles;
          private List<File> remFiles;
          private boolean doFire = true;
          private int fid;
          private int addStart = 0;
          private int remStart = 0;
  
          public DoChangeContents(List<File> addFiles, int addStart, List<File> remFiles, int remStart, int fid) {
              this.addFiles = addFiles;
              this.addStart = addStart;
              this.remFiles = remFiles;
              this.remStart = remStart;
              this.fid = fid;
          }
  
          synchronized void cancel() {
                  doFire = false;
          }
  
          public synchronized void run() {
              if (fetchID == fid && doFire) {
                  int remSize = (remFiles == null) ? 0 : remFiles.size();
                  int addSize = (addFiles == null) ? 0 : addFiles.size();
                  synchronized(fileCache) {
                      if (remSize > 0) {
                          fileCache.removeAll(remFiles);
                      }
                      if (addSize > 0) {
                          fileCache.addAll(addStart, addFiles);
                      }
                      files = null;
                      directories = null;
                  }
  