gt:                 if (isAdd) {
context: /**
   * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
   * Licensed under the terms of the Eclipse Public License (EPL).
   * Please see the license.txt included with this distribution for details.
   * Any modifications to this file must keep this entire header intact.
   */
  /*
   * Created on Oct 7, 2006
   * @author Fabio
   */
  package org.python.pydev.navigator;
  
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.LinkedHashSet;
  import java.util.Set;
  
  import org.eclipse.core.resources.IContainer;
  import org.eclipse.core.resources.IFile;
  import org.eclipse.core.resources.IFolder;
  import org.eclipse.core.resources.IProject;
  import org.eclipse.core.resources.IResource;
  import org.eclipse.core.resources.IWorkspaceRoot;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IPath;
  import org.eclipse.ui.IMemento;
  import org.eclipse.ui.IWorkingSet;
  import org.eclipse.ui.navigator.IPipelinedTreeContentProvider;
  import org.eclipse.ui.navigator.PipelinedShapeModification;
  import org.eclipse.ui.navigator.PipelinedViewerUpdate;
  import org.python.pydev.core.log.Log;
  import org.python.pydev.navigator.elements.IWrappedResource;
  import org.python.pydev.navigator.elements.ProjectConfigError;
  import org.python.pydev.navigator.elements.PythonFile;
  import org.python.pydev.navigator.elements.PythonFolder;
  import org.python.pydev.navigator.elements.PythonProjectSourceFolder;
  import org.python.pydev.navigator.elements.PythonResource;
  import org.python.pydev.navigator.elements.PythonSourceFolder;
  import org.python.pydev.plugin.nature.PythonNature;
  import org.python.pydev.shared_core.structure.FastStack;
  import org.python.pydev.shared_core.structure.TreeNode;
  
  /**
   * This is the Model provider for python elements.
   *
   * It intercepts the adds/removes and changes the original elements for elements
   * that actually reflect the python model (with source folder, etc).
   *
   *
   * Tests for package explorer:
   * 1. Start eclipse with a file deep in the structure and without having anything expanded in the tree, make a 'show in'
   *
   * @author Fabio
   */
  public final class PythonModelProvider extends PythonBaseModelProvider implements IPipelinedTreeContentProvider {
  
      /* (non-Javadoc)
       * @see org.python.pydev.navigator.PythonBaseModelProvider#getChildren(java.lang.Object)
       */
      @Override
      public Object[] getChildren(Object parentElement) {
          Object[] ret = super.getChildren(parentElement);
          if (parentElement instanceof PythonProjectSourceFolder) {
              PythonProjectSourceFolder projectSourceFolder = (PythonProjectSourceFolder) parentElement;
              Set<Object> set = new HashSet<Object>();
              fillChildrenForProject(set, (IProject) projectSourceFolder.getActualObject(), projectSourceFolder);
              if (set.size() > 0) {
                  Object[] newRet = new Object[ret.length + set.size()];
                  System.arraycopy(ret, 0, newRet, 0, ret.length);
                  int i = ret.length;
                  for (Object o : set) {
                      newRet[i] = o;
                      i++;
                  }
                  ret = newRet;
              }
          }
          return ret;
      }
  
      /**
       * This method basically replaces all the elements for other resource elements
       * or for wrapped elements.
       *
       * @see org.eclipse.ui.navigator.IPipelinedTreeContentProvider#getPipelinedChildren(java.lang.Object, java.util.Set)
       */
      @Override
      @SuppressWarnings("unchecked")
      public void getPipelinedChildren(Object parent, Set currentElements) {
          if (DEBUG) {
              System.out.println("getPipelinedChildren for: " + parent);
          }
  
          if (parent instanceof IWrappedResource) {
              //Note: It seems that this NEVER happens (IWrappedResources only have getChildren called, not getPipelinedChildren)
              Object[] children = getChildren(parent);
              currentElements.clear();
              currentElements.addAll(Arrays.asList(children));
              if (DEBUG) {
                  System.out.println("getPipelinedChildren RETURN: " + currentElements);
              }
              if (parent instanceof PythonProjectSourceFolder) {
                  PythonProjectSourceFolder projectSourceFolder = (PythonProjectSourceFolder) parent;
                  IProject project = (IProject) projectSourceFolder.getActualObject();
                  fillChildrenForProject(currentElements, project, parent);
              }
              return;
  
          } else if (parent instanceof IWorkspaceRoot) {
              switch (topLevelChoice.getRootMode()) {
                  case TopLevelProjectsOrWorkingSetChoice.WORKING_SETS:
                      currentElements.clear();
                      currentElements.addAll(getWorkingSetsCallback.call((IWorkspaceRoot) parent));
                      if (currentElements.size() == 0) {
                          currentElements.add(createErrorNoWorkingSetsDefined(parent));
                      }
                  case TopLevelProjectsOrWorkingSetChoice.PROJECTS:
                      //Just go on...
              }
  
          } else if (parent instanceof IWorkingSet) {
              IWorkingSet workingSet = (IWorkingSet) parent;
              currentElements.clear();
              currentElements.addAll(Arrays.asList(workingSet.getElements()));
              if (currentElements.size() == 0) {
                  currentElements.add(createErrorNoWorkingSetsDefined(workingSet));
              }
  
          } else if (parent instanceof TreeNode) {
              TreeNode treeNode = (TreeNode) parent;
              currentElements.addAll(treeNode.getChildren());
  
          } else if (parent instanceof IProject) {
              IProject project = (IProject) parent;
              fillChildrenForProject(currentElements, project, getResourceInPythonModel(project));
          }
  
          PipelinedShapeModification modification = new PipelinedShapeModification(parent, currentElements);
          convertToPythonElementsAddOrRemove(modification, true);
          if (DEBUG) {
              System.out.println("getPipelinedChildren RETURN: " + modification.getChildren());
          }
      }
  
      @SuppressWarnings("unchecked")
      private void fillChildrenForProject(Set currentElements, IProject project, Object parent) {
          ProjectInfoForPackageExplorer projectInfo = ProjectInfoForPackageExplorer.getProjectInfo(project);
          if (projectInfo != null) {
              currentElements.addAll(projectInfo.configErrors);
              InterpreterInfoTreeNode<LabelAndImage> projectInfoTreeStructure = projectInfo.getProjectInfoTreeStructure(
                      project, parent);
              if (projectInfoTreeStructure != null) {
                  currentElements.add(projectInfoTreeStructure);
              }
          }
      }
  
      /**
       * This method basically replaces all the elements for other resource elements
       * or for wrapped elements.
       *
       * @see org.eclipse.ui.navigator.IPipelinedTreeContentProvider#getPipelinedElements(java.lang.Object, java.util.Set)
       */
      @Override
      public void getPipelinedElements(Object input, Set currentElements) {
          if (DEBUG) {
              System.out.println("getPipelinedElements for: " + input);
          }
          getPipelinedChildren(input, currentElements);
      }
  
      /**
       * This method basically get the actual parent for the resource or the parent
       * for a wrapped element (which may be a resource or a wrapped resource).
       *
       * @see org.eclipse.ui.navigator.IPipelinedTreeContentProvider#getPipelinedParent(java.lang.Object, java.lang.Object)
       */
      @Override
      public Object getPipelinedParent(Object object, Object aSuggestedParent) {
          if (DEBUG) {
              System.out.println("getPipelinedParent for: " + object);
          }
          //Now, we got the parent for the resources correctly at this point, but there's one last thing we may need to
          //do: the actual parent may be a working set!
          Object p = this.topLevelChoice.getWorkingSetParentIfAvailable(object, getWorkingSetsCallback);
          if (p != null) {
              aSuggestedParent = p;
  
          } else if (object instanceof IWrappedResource) {
              IWrappedResource resource = (IWrappedResource) object;
              Object parentElement = resource.getParentElement();
              if (parentElement != null) {
                  aSuggestedParent = parentElement;
              }
  
          } else if (object instanceof TreeNode<?>) {
              TreeNode<?> treeNode = (TreeNode<?>) object;
              return treeNode.getParent();
  
          } else if (object instanceof ProjectConfigError) {
              ProjectConfigError configError = (ProjectConfigError) object;
              return configError.getParent();
  
          }
  
          if (DEBUG) {
              System.out.println("getPipelinedParent RETURN: " + aSuggestedParent);
          }
          return aSuggestedParent;
      }
  
      /**
       * This method intercepts some addition to the tree and converts its elements to python
       * elements.
       *
       * @see org.eclipse.ui.navigator.IPipelinedTreeContentProvider#interceptAdd(org.eclipse.ui.navigator.PipelinedShapeModification)
       */
      @Override
      public PipelinedShapeModification interceptAdd(PipelinedShapeModification addModification) {
          if (DEBUG) {
              System.out.println("interceptAdd");
          }
          convertToPythonElementsAddOrRemove(addModification, true);
          return addModification;
      }
  
      @Override
      public boolean interceptRefresh(PipelinedViewerUpdate refreshSynchronization) {
          if (DEBUG) {
              System.out.println("interceptRefresh");
          }
          return convertToPythonElementsUpdateOrRefresh(refreshSynchronization.getRefreshTargets());
      }
  
      @Override
      public PipelinedShapeModification interceptRemove(PipelinedShapeModification removeModification) {
          if (DEBUG) {
              System.out.println("interceptRemove");
          }
          convertToPythonElementsAddOrRemove(removeModification, false);
          return removeModification;
      }
  
      @Override
      public boolean interceptUpdate(PipelinedViewerUpdate updateSynchronization) {
          if (DEBUG) {
              debug("Before interceptUpdate", updateSynchronization);
          }
          boolean ret = convertToPythonElementsUpdateOrRefresh(updateSynchronization.getRefreshTargets());
          if (DEBUG) {
              debug("After interceptUpdate", updateSynchronization);
          }
          return ret;
      }
  
      /**
       * Helper for debugging the things we have in an update
       */
      private void debug(String desc, PipelinedViewerUpdate updateSynchronization) {
          System.out.println("\nDesc:" + desc);
          System.out.println("Refresh targets:");
          for (Object o : updateSynchronization.getRefreshTargets()) {
              System.out.println(o);
          }
      }
  
      /**
       * Helper for debugging the things we have in a modification
       */
      private void debug(String desc, PipelinedShapeModification modification) {
          System.out.println("\nDesc:" + desc);
          Object parent = modification.getParent();
          System.out.println("Parent:" + parent);
          System.out.println("Children:");
          for (Object o : modification.getChildren()) {
              System.out.println(o);
          }
      }
  
      /**
       * This is the function that is responsible for restoring the paths in the tree.
       */
      @Override
      public void restoreState(IMemento memento) {
          new PyPackageStateSaver(this, viewer, memento).restoreState();
      }
  
      /**
       * This is the function that is responsible for saving the paths in the tree.
       */
      @Override
      public void saveState(IMemento memento) {
          new PyPackageStateSaver(this, viewer, memento).saveState();
      }
  
      /**
       * Converts the shape modification to use Python elements.
       *
       * @param modification: the shape modification to convert
       * @param isAdd: boolean indicating whether this convertion is happening in an add operation
       */
      private void convertToPythonElementsAddOrRemove(PipelinedShapeModification modification, boolean isAdd) {
          if (DEBUG) {
              debug("Before", modification);
          }
          Object parent = modification.getParent();
          if (parent instanceof IContainer) {
              IContainer parentContainer = (IContainer) parent;
              Object pythonParent = getResourceInPythonModel(parentContainer, true);
  
              if (pythonParent instanceof IWrappedResource) {
                  IWrappedResource parentResource = (IWrappedResource) pythonParent;
                  modification.setParent(parentResource);
                  wrapChildren(parentResource, parentResource.getSourceFolder(), modification.getChildren(), isAdd);
  
              } else if (pythonParent == null) {
  
                  Object parentInWrap = parentContainer;
                  PythonSourceFolder sourceFolderInWrap = null;
  
                  //this may happen when a source folder is added or some element that still doesn't have it's parent in the model...
                  //so, we have to get the parent's parent until we actually 'know' that it is not in the model (or until we run
                  //out of parents to try)
                  //the case in which we reproduce this is Test 1 (described in the class)
                  FastStack<Object> found = new FastStack<Object>(20);
                  while (true) {
  
                      //add the current to the found
                      if (parentContainer == null) {
                          break;
                      }
  
                      found.push(parentContainer);
                      if (parentContainer instanceof IProject) {
                          //we got to the project without finding any part of a python model already there, so, let's see
                          //if any of the parts was actually a source folder (that was still not added)
                          tryCreateModelFromProject((IProject) parentContainer, found);
                          //and now, if it was created, try to convert it to the python model (without any further add)
                          convertToPythonElementsUpdateOrRefresh(modification.getChildren());
                          return;
                      }
  
                      Object p = getResourceInPythonModel(parentContainer, true);
  
                      if (p instanceof IWrappedResource) {
                          IWrappedResource wrappedResource = (IWrappedResource) p;
                          sourceFolderInWrap = wrappedResource.getSourceFolder();
  
                          while (found.size() > 0) {
                              Object f = found.pop();
                              if (f instanceof IResource) {
                                  //no need to create it if it's already in the model!
                                  Object child = sourceFolderInWrap.getChild((IResource) f);
                                  if (child != null && child instanceof IWrappedResource) {
                                      wrappedResource = (IWrappedResource) child;
                                      continue;
                                  }
                              }
                              //creating is enough to add it to the model
                              if (f instanceof IFile) {
                                  wrappedResource = new PythonFile(wrappedResource, (IFile) f, sourceFolderInWrap);
                              } else if (f instanceof IFolder) {
                                  wrappedResource = new PythonFolder(wrappedResource, (IFolder) f, sourceFolderInWrap);
                              }
                          }
                          parentInWrap = wrappedResource;
                          break;
                      }
  
                      parentContainer = parentContainer.getParent();
                  }
  
                  wrapChildren(parentInWrap, sourceFolderInWrap, modification.getChildren(), isAdd);
              }
  
          } else if (parent == null) {
              wrapChildren(null, null, modification.getChildren(), isAdd);
          }
  
          if (DEBUG) {
              debug("After", modification);
          }
      }
  
      /**
       * Given a Path from the 1st child of the project, will try to create that path in the python model.
       * @param project the project
       * @param found a stack so that the last element added is the leaf of the path we want to discover
       */
      private void tryCreateModelFromProject(IProject project, FastStack<Object> found) {
          PythonNature nature = PythonNature.getPythonNature(project);
          if (nature == null) {
              return;//if the python nature is not available, we won't have any python elements here
          }
          Set<String> sourcePathSet = new HashSet<String>();
          try {
              sourcePathSet = nature.getPythonPathNature().getProjectSourcePathSet(true);
          } catch (CoreException e) {
              Log.log(e);
          }
  
          Object currentParent = project;
          PythonSourceFolder pythonSourceFolder = null;
          for (Iterator<Object> it = found.topDownIterator(); it.hasNext();) {
              Object child = it.next();
              if (child instanceof IFolder || child instanceof IProject) {
                  if (pythonSourceFolder == null) {
                      pythonSourceFolder = tryWrapSourceFolder(currentParent, (IContainer) child, sourcePathSet);
  
                      if (pythonSourceFolder != null) {
                          currentParent = pythonSourceFolder;
  
                      } else if (child instanceof IContainer) {
                          currentParent = child;
  
                      }
  
                      //just go on (if we found the source folder or not, because if we found, that's ok, and if
                      //we didn't, then the children will not be in the python model anyway)
                      continue;
                  }
              }
  
              if (pythonSourceFolder != null) {
                  IWrappedResource r = doWrap(currentParent, pythonSourceFolder, child);
                  if (r != null) {
                      child = r;
                  }
              }
              currentParent = child;
          }
      }
  
      /**
       * Actually wraps some resource into a wrapped resource.
       *
       * @param parent this is the parent
       *        it may be null -- in the case of a remove
       *        it may be a wrapped resource (if it is in the python model)
       *        it may be a resource (if it is a source folder)
       *
       *
       * @param pythonSourceFolder this is the python source folder for the resource (it may be null if the resource itself is a source folder
       *        or if it is actually a resource that has already been removed)
       * @param currentChildren those are the children that should be wrapped
       * @param isAdd whether this is an add operation or not
       * @return
       */
      @SuppressWarnings("unchecked")
      protected boolean wrapChildren(Object parent, PythonSourceFolder pythonSourceFolder, Set currentChildren,
              boolean isAdd) {
          LinkedHashSet convertedChildren = new LinkedHashSet();
  
          for (Iterator childrenItr = currentChildren.iterator(); childrenItr.hasNext();) {
              Object child = childrenItr.next();
  
              if (child == null) {
                  //only case when a child is removed and another one is not added (null)
                  childrenItr.remove();
                  continue;
              }
  
              //yeap, it may be an object that's not an actual resource (created by some other plugin... just continue)
              if (!(child instanceof IResource)) {
                  continue;
              }
              Object existing = getResourceInPythonModel((IResource) child, true);
  
              if (existing == null) {
  