/**
   * Bug 68993: [Preferences] IAE when opening project preferences
   *
   * @see "http://bugs.eclipse.org/bugs/show_bug.cgi?id=68993"
   */
  public void testBug68993() throws CoreException, BackingStoreException {
    try {
      JavaProject projectA =
          (JavaProject)
              this.createJavaProject(
                  "A",
                  new String[] {}, // source folders
                  new String[] {}, // lib folders
                  new String[] {}, // projects
                  "");

      // set all project options as custom ones: this is what happens when user select
      // "Use project settings" in project 'Java Compiler' preferences page...
      Hashtable options = new Hashtable(projectA.getOptions(true));
      projectA.setOptions(options);

      // reset all project custom options: this is what happens when user select
      // "Use workspace settings" in project 'Java Compiler' preferences page...
      options = new Hashtable();
      options.put("internal.default.compliance", JavaCore.DEFAULT);
      projectA.setOptions(options);

      // verify that project preferences have been reset
      assertEquals(
          "projA: We should not have any custom options!",
          0,
          projectA.getEclipsePreferences().keys().length);
    } finally {
      this.deleteProject("A");
    }
  }
  /**
   * Custom options must replace existing ones completely without loosing property listeners
   * http://bugs.eclipse.org/bugs/show_bug.cgi?id=59258
   * http://bugs.eclipse.org/bugs/show_bug.cgi?id=60896
   */
  public void test09() throws CoreException {
    try {
      this.eventCount = 0;
      JavaProject projectA = (JavaProject) this.createJavaProject("A", new String[] {}, "");
      //		Preferences preferences = projectA.getPreferences();
      //		preferences.addPropertyChangeListener(new TestPropertyListener());
      IEclipsePreferences eclipsePreferences = projectA.getEclipsePreferences();
      eclipsePreferences.addPreferenceChangeListener(new TestPropertyListener());

      Hashtable options = new Hashtable();
      options.put(JavaCore.COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE, JavaCore.ENABLED);
      options.put(JavaCore.COMPILER_COMPLIANCE, "10.0");
      projectA.setOptions(options);

      // check project A custom options
      assertEquals(
          "projA:unexpected custom value for deprecation option",
          JavaCore.ENABLED,
          projectA.getOptions(true).get(JavaCore.COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE));
      assertEquals(
          "projA:unexpected custom value for compliance option",
          "10.0",
          projectA.getOptions(true).get(JavaCore.COMPILER_COMPLIANCE));
      assertTrue(
          "projA:preferences should not be reset",
          eclipsePreferences == projectA.getEclipsePreferences());
      assertEquals("projA:preferences property listener has been lost", 2, this.eventCount);

      // delete/create project A and verify that options are well reset
      this.deleteProject("A");
      projectA = (JavaProject) this.createJavaProject("A", new String[] {}, "");
      assertEquals(
          "projA:unexpected custom value for deprecation option",
          JavaCore.getOption(JavaCore.COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE),
          projectA.getOptions(true).get(JavaCore.COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE));
      assertEquals(
          "projA:unexpected custom value for compliance option",
          JavaCore.getOption(JavaCore.COMPILER_COMPLIANCE),
          projectA.getOptions(true).get(JavaCore.COMPILER_COMPLIANCE));
      assertTrue(
          "projA:preferences should not be reset",
          eclipsePreferences != projectA.getEclipsePreferences());
    } finally {
      this.deleteProject("A");
    }
  }
  /**
   * @bug 346010 - [model] strange initialization dependency in OptionTests
   * @test Verify that unfortunate order of map entries doesn't spoil intended semantics.
   * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=346010"
   * @deprecated As using deprecated constants
   */
  public void testBug346010() throws CoreException {
    class ForcedOrderMap extends Hashtable {
      private static final long serialVersionUID = 8012963985718522218L;
      Map original;
      Map.Entry additionalEntry;
      /* Force (additionalKey,additionalValue) to be served after all entries of original. */
      public ForcedOrderMap(Map original, String additionalKey, String additionalValue) {
        this.original = original;
        // convert additionalKey->additionalValue to a Map.Entry without inserting into original:
        Hashtable tmp = new Hashtable();
        tmp.put(additionalKey, additionalValue);
        this.additionalEntry = (Map.Entry) tmp.entrySet().iterator().next();
      }

      public Set entrySet() {
        return new HashSet() {
          private static final long serialVersionUID = 1L;

          public Iterator iterator() {
            List orderedEntries;
            orderedEntries = new ArrayList(ForcedOrderMap.this.original.entrySet());
            orderedEntries.add(ForcedOrderMap.this.additionalEntry);
            return orderedEntries.iterator();
          }
        };
      }

      public synchronized boolean containsKey(Object key) {
        return this.original.containsKey(key) || key.equals(this.additionalEntry.getKey());
      }
    }
    try {
      // Set the obsolete option using the IJavaProject API
      JavaProject project = (JavaProject) createJavaProject("P");
      final String obsoleteOption =
          DefaultCodeFormatterConstants.FORMATTER_INSERT_NEW_LINE_AFTER_ANNOTATION_ON_MEMBER;
      Map testOptions = project.getOptions(true);
      Map orderedOptions = new ForcedOrderMap(testOptions, obsoleteOption, JavaCore.DO_NOT_INSERT);
      project.setOptions(orderedOptions);
      // Verify that obsolete preference is not stored
      assertNull(
          "Unexpected value for formatter deprecated option 'org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member'",
          project.getEclipsePreferences().get(obsoleteOption, null));
      // Verify that project obsolete option is well retrieved
      assertEquals(
          "Unexpected value for formatter deprecated option 'org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member'",
          JavaCore.INSERT,
          project.getOption(obsoleteOption, true));
    } finally {
      deleteProject("P");
    }
  }
 /**
  * @bug 324987: [formatter] API compatibility problem with Annotation Newline options
  * @test Verify that a new option beats the deprecated option when a client sets both through the
  *     IJavaProject#setOptions(Map) API
  * @see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=324987"
  * @deprecated As using deprecated constants
  */
 public void testBug324987_Project02() throws CoreException {
   try {
     // Set the obsolete option using the IJavaProject API
     JavaProject project = (JavaProject) createJavaProject("P");
     final String obsoleteOption =
         DefaultCodeFormatterConstants.FORMATTER_INSERT_NEW_LINE_AFTER_ANNOTATION_ON_MEMBER;
     Map testOptions = project.getOptions(true);
     testOptions.put(obsoleteOption, JavaCore.DO_NOT_INSERT);
     project.setOptions(testOptions);
     // Verify that obsolete preference is not stored
     assertNull(
         "Unexpected value for formatter deprecated option 'org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member'",
         project.getEclipsePreferences().get(obsoleteOption, null));
     // Verify that project obsolete option is well retrieved
     assertEquals(
         "Unexpected value for formatter deprecated option 'org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member'",
         JavaCore.INSERT,
         project.getOption(obsoleteOption, true));
   } finally {
     deleteProject("P");
   }
 }
  private void buildForProject(
      JavaProject project,
      ArrayList potentialSubtypes,
      org.eclipse.jdt.core.ICompilationUnit[] workingCopies,
      HashSet localTypes,
      IProgressMonitor monitor)
      throws JavaModelException {
    // resolve
    int openablesLength = potentialSubtypes.size();
    if (openablesLength > 0) {
      // copy vectors into arrays
      Openable[] openables = new Openable[openablesLength];
      potentialSubtypes.toArray(openables);

      // sort in the order of roots and in reverse alphabetical order for .class file
      // since requesting top level types in the process of caching an enclosing type is
      // not supported by the lookup environment
      IPackageFragmentRoot[] roots = project.getPackageFragmentRoots();
      int rootsLength = roots.length;
      final HashtableOfObjectToInt indexes = new HashtableOfObjectToInt(openablesLength);
      for (int i = 0; i < openablesLength; i++) {
        IJavaElement root = openables[i].getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
        int index;
        for (index = 0; index < rootsLength; index++) {
          if (roots[index].equals(root)) break;
        }
        indexes.put(openables[i], index);
      }
      Arrays.sort(
          openables,
          new Comparator() {
            public int compare(Object a, Object b) {
              int aIndex = indexes.get(a);
              int bIndex = indexes.get(b);
              if (aIndex != bIndex) return aIndex - bIndex;
              return ((Openable) b).getElementName().compareTo(((Openable) a).getElementName());
            }
          });

      IType focusType = getType();
      boolean inProjectOfFocusType =
          focusType != null && focusType.getJavaProject().equals(project);
      org.eclipse.jdt.core.ICompilationUnit[] unitsToLookInside = null;
      if (inProjectOfFocusType) {
        org.eclipse.jdt.core.ICompilationUnit unitToLookInside = focusType.getCompilationUnit();
        if (unitToLookInside != null) {
          int wcLength = workingCopies == null ? 0 : workingCopies.length;
          if (wcLength == 0) {
            unitsToLookInside = new org.eclipse.jdt.core.ICompilationUnit[] {unitToLookInside};
          } else {
            unitsToLookInside = new org.eclipse.jdt.core.ICompilationUnit[wcLength + 1];
            unitsToLookInside[0] = unitToLookInside;
            System.arraycopy(workingCopies, 0, unitsToLookInside, 1, wcLength);
          }
        } else {
          unitsToLookInside = workingCopies;
        }
      }

      SearchableEnvironment searchableEnvironment =
          project.newSearchableNameEnvironment(unitsToLookInside);
      this.nameLookup = searchableEnvironment.nameLookup;
      Map options = project.getOptions(true);
      // disable task tags to speed up parsing
      options.put(JavaCore.COMPILER_TASK_TAGS, ""); // $NON-NLS-1$
      this.hierarchyResolver =
          new HierarchyResolver(searchableEnvironment, options, this, new DefaultProblemFactory());
      if (focusType != null) {
        Member declaringMember = ((Member) focusType).getOuterMostLocalContext();
        if (declaringMember == null) {
          // top level or member type
          if (!inProjectOfFocusType) {
            char[] typeQualifiedName = focusType.getTypeQualifiedName('.').toCharArray();
            String[] packageName = ((PackageFragment) focusType.getPackageFragment()).names;
            if (searchableEnvironment.findType(typeQualifiedName, Util.toCharArrays(packageName))
                == null) {
              // focus type is not visible in this project: no need to go further
              return;
            }
          }
        } else {
          // local or anonymous type
          Openable openable;
          if (declaringMember.isBinary()) {
            openable = (Openable) declaringMember.getClassFile();
          } else {
            openable = (Openable) declaringMember.getCompilationUnit();
          }
          localTypes = new HashSet();
          localTypes.add(openable.getPath().toString());
          this.hierarchyResolver.resolve(new Openable[] {openable}, localTypes, monitor);
          return;
        }
      }
      this.hierarchyResolver.resolve(openables, localTypes, monitor);
    }
  }
  /**
   * Custom options must replace existing ones completely without loosing property listeners
   * http://bugs.eclipse.org/bugs/show_bug.cgi?id=26255
   * http://bugs.eclipse.org/bugs/show_bug.cgi?id=49691
   */
  public void test07() throws CoreException {
    try {
      this.eventCount = 0;
      JavaProject projectA =
          (JavaProject)
              this.createJavaProject(
                  "A",
                  new String[] {}, // source folders
                  new String[] {}, // lib folders
                  new String[] {}, // projects
                  "");
      //		Preferences preferences = projectA.getPreferences();
      //		preferences.addPropertyChangeListener(new TestPropertyListener());
      IEclipsePreferences eclipsePreferences = projectA.getEclipsePreferences();
      TestPropertyListener listener = new TestPropertyListener();
      eclipsePreferences.addPreferenceChangeListener(listener);

      Hashtable options = new Hashtable();
      options.put(JavaCore.COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE, JavaCore.ENABLED);
      options.put(JavaCore.COMPILER_COMPLIANCE, "10.0");
      projectA.setOptions(options);

      // check project A custom options
      assertEquals(
          "projA:unexpected custom value for deprecation option",
          JavaCore.ENABLED,
          projectA.getOptions(false).get(JavaCore.COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE));
      assertEquals(
          "projA:unexpected custom value for compliance option",
          "10.0",
          projectA.getOptions(false).get(JavaCore.COMPILER_COMPLIANCE));
      assertEquals(
          "projA:unexpected inherited value1 for hidden-catch option",
          null,
          projectA.getOptions(false).get(JavaCore.COMPILER_PB_HIDDEN_CATCH_BLOCK));
      //		assertTrue("projA:preferences should not be reset", preferences ==
      // projectA.getPreferences());
      assertTrue(
          "projA:preferences should not be reset",
          eclipsePreferences == projectA.getEclipsePreferences());
      assertTrue("projA:preferences property listener has been lost", this.eventCount == 2);

      // change custom options to have one less
      options.clear();
      options.put(JavaCore.COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE, JavaCore.ENABLED);
      projectA.setOptions(options);
      assertEquals(
          "projA:unexpected custom value for deprecation option",
          JavaCore.ENABLED,
          projectA.getOptions(false).get(JavaCore.COMPILER_PB_DEPRECATION_IN_DEPRECATED_CODE));
      assertEquals(
          "projA:unexpected custom value for compliance option",
          null,
          projectA.getOptions(false).get(JavaCore.COMPILER_COMPLIANCE));
      assertEquals(
          "projA:unexpected inherited value1 for hidden-catch option",
          null,
          projectA.getOptions(false).get(JavaCore.COMPILER_PB_HIDDEN_CATCH_BLOCK));
      //		assertTrue("projA:preferences should not be reset", preferences ==
      // projectA.getPreferences());
      assertTrue(
          "projA:preferences should not be reset",
          eclipsePreferences == projectA.getEclipsePreferences());
      assertTrue("projA:preferences property listener has been lost", this.eventCount == 3);
    } finally {
      this.deleteProject("A");
    }
  }
  @SuppressWarnings({"unchecked", "rawtypes", "nls", "restriction"})
  @Override
  protected boolean buildStructure(
      OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource)
      throws JavaModelException {
    try {
      depth.set(depth.get() + 1);

      // if (!isOnBuildPath()) {
      // return false;
      // }

      if (GroovyLogManager.manager.hasLoggers()) {
        GroovyLogManager.manager.log(
            TraceCategory.COMPILER, "Build Structure starting for " + this.name);
        GroovyLogManager.manager.logStart(
            "Build structure: " + name + " : " + Thread.currentThread().getName());
      }

      CompilationUnitElementInfo unitInfo = (CompilationUnitElementInfo) info;

      // ensure buffer is opened
      IBuffer buffer = getBufferManager().getBuffer(this);
      if (buffer == null) {
        openBuffer(pm, unitInfo); // open buffer independently from the
        // info, since we are building the info
      }

      // generate structure and compute syntax problems if needed
      GroovyCompilationUnitStructureRequestor requestor =
          new GroovyCompilationUnitStructureRequestor(this, unitInfo, newElements);
      JavaModelManager.PerWorkingCopyInfo perWorkingCopyInfo = getPerWorkingCopyInfo();
      JavaProject project = (JavaProject) getJavaProject();

      // determine what kind of buildStructure we are doing
      boolean createAST;
      int reconcileFlags;
      boolean resolveBindings;
      HashMap problems;
      if (info instanceof ASTHolderCUInfo) {
        ASTHolderCUInfo astHolder = (ASTHolderCUInfo) info;
        createAST =
            ((Integer)
                        ReflectionUtils.getPrivateField(
                            ASTHolderCUInfo.class, "astLevel", astHolder))
                    .intValue()
                != NO_AST; //$NON-NLS-1$
        resolveBindings =
            ((Boolean)
                    ReflectionUtils.getPrivateField(
                        ASTHolderCUInfo.class, "resolveBindings", astHolder))
                .booleanValue();
        reconcileFlags =
            ((Integer)
                    ReflectionUtils.getPrivateField(
                        ASTHolderCUInfo.class, "reconcileFlags", astHolder)) // $NON-NLS-1$
                .intValue();
        problems =
            (HashMap) ReflectionUtils.getPrivateField(ASTHolderCUInfo.class, "problems", astHolder);
      } else {
        createAST = false;
        resolveBindings = false;
        reconcileFlags = 0;
        problems = null;
      }

      boolean computeProblems =
          perWorkingCopyInfo != null
              && perWorkingCopyInfo.isActive()
              && project != null
              && JavaProject.hasJavaNature(project.getProject());
      IProblemFactory problemFactory = new DefaultProblemFactory();

      // compiler options
      Map<String, String> options =
          (project == null ? JavaCore.getOptions() : project.getOptions(true));
      if (!computeProblems) {
        // disable task tags checking to speed up parsing
        options.put(JavaCore.COMPILER_TASK_TAGS, ""); // $NON-NLS-1$
      }

      // FIXASC deal with the case of project==null to reduce duplication in this next line and call
      // to setGroovyClasspath
      // Required for Groovy, but not for Java
      options.put(CompilerOptions.OPTIONG_BuildGroovyFiles, CompilerOptions.ENABLED);

      CompilerOptions compilerOptions = new CompilerOptions(options);

      if (project != null) {
        CompilerUtils.setGroovyClasspath(compilerOptions, project);
      }

      // Required for Groovy, but not for Java
      ProblemReporter reporter =
          new ProblemReporter(
              new GroovyErrorHandlingPolicy(!computeProblems),
              compilerOptions,
              new DefaultProblemFactory());

      SourceElementParser parser =
          new MultiplexingSourceElementRequestorParser(
              reporter,
              requestor, /*
                          * not needed if
                          * computing groovy only
                          */
              problemFactory,
              compilerOptions,
              true /* report local declarations */,
              !createAST /*
                          * optimize string literals only if not
                          * creating a DOM AST
                          */);
      parser.reportOnlyOneSyntaxError = !computeProblems;
      // maybe not needed for groovy, but I don't want to find out.
      parser.setMethodsFullRecovery(true);
      parser.setStatementsRecovery(
          (reconcileFlags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0);

      if (!computeProblems
          && !resolveBindings
          && !createAST) // disable javadoc parsing if not computing problems, not
        // resolving
        // and not creating ast
        parser.javadocParser.checkDocComment = false;
      requestor.setParser(parser);

      // update timestamp (might be IResource.NULL_STAMP if original does not
      // exist)
      if (underlyingResource == null) {
        underlyingResource = getResource();
      }
      // underlying resource is null in the case of a working copy on a class
      // file in a jar
      if (underlyingResource != null) {
        ReflectionUtils.setPrivateField(
            CompilationUnitElementInfo.class,
            "timestamp",
            unitInfo,
            underlyingResource //$NON-NLS-1$
                .getModificationStamp());
      }

      GroovyCompilationUnitDeclaration compilationUnitDeclaration = null;
      CompilationUnit source = cloneCachingContents();
      try {
        // GROOVY
        // note that this is a slightly different approach than taken by super.buildStructure
        // in super.buildStructure, there is a test here to see if computeProblems is true.
        // if false, then parser.parserCompilationUnit is called.
        // this will not work for Groovy because we need to ensure bindings are resolved
        // for many operations (content assist and code select) to work.
        // So, for groovy, always use CompilationUnitProblemFinder.process and then process problems
        // separately only if necessary
        // addendum (GRECLIPSE-942). The testcase for that bug includes a package with 200
        // types in that refer to each other in a chain, through field references. If a reconcile
        // references the top of the chain we can go through a massive number of recursive calls
        // into
        // this buildStructure for each one. The 'full' parse (with bindings) is only required for
        // the top most (regardless of the computeProblems setting) and so we track how many
        // recursive
        // calls we have made - if we are at depth 2 we do what JDT was going to do (the quick
        // thing).
        if (computeProblems || depth.get() < 2) {
          if (problems == null) {
            // report problems to the problem requestor
            problems = new HashMap();
            compilationUnitDeclaration =
                (GroovyCompilationUnitDeclaration)
                    CompilationUnitProblemFinder.process(
                        source, parser, this.owner, problems, createAST, reconcileFlags, pm);
            if (computeProblems) {
              try {
                perWorkingCopyInfo.beginReporting();
                for (Iterator iteraror = problems.values().iterator(); iteraror.hasNext(); ) {
                  CategorizedProblem[] categorizedProblems = (CategorizedProblem[]) iteraror.next();
                  if (categorizedProblems == null) continue;
                  for (int i = 0, length = categorizedProblems.length; i < length; i++) {
                    perWorkingCopyInfo.acceptProblem(categorizedProblems[i]);
                  }
                }
              } finally {
                perWorkingCopyInfo.endReporting();
              }
            }
          } else {
            // collect problems
            compilationUnitDeclaration =
                (GroovyCompilationUnitDeclaration)
                    CompilationUnitProblemFinder.process(
                        source, parser, this.owner, problems, createAST, reconcileFlags, pm);
          }
        } else {
          compilationUnitDeclaration =
              (GroovyCompilationUnitDeclaration)
                  parser.parseCompilationUnit(
                      source, true /* full parse to find local elements */, pm);
        }

        // GROOVY
        // if this is a working copy, then we have more work to do
        maybeCacheModuleNode(perWorkingCopyInfo, compilationUnitDeclaration);

        // create the DOM AST from the compiler AST
        if (createAST) {
          org.eclipse.jdt.core.dom.CompilationUnit ast;
          try {
            ast =
                AST.convertCompilationUnit(
                    AST.JLS3,
                    compilationUnitDeclaration,
                    options,
                    computeProblems,
                    source,
                    reconcileFlags,
                    pm);
            ReflectionUtils.setPrivateField(ASTHolderCUInfo.class, "ast", info, ast); // $NON-NLS-1$
          } catch (OperationCanceledException e) {
            // catch this exception so as to not enter the catch(RuntimeException e) below
            // might need to do the same for AbortCompilation
            throw e;
          } catch (IllegalArgumentException e) {
            // if necessary, we can do some better reporting here.
            Util.log(
                e,
                "Problem with build structure: Offset for AST node is incorrect in " //$NON-NLS-1$
                    + this.getParent().getElementName()
                    + "."
                    + getElementName()); //$NON-NLS-1$
          } catch (Exception e) {
            Util.log(e, "Problem with build structure for " + this.getElementName()); // $NON-NLS-1$
          }
        }
      } catch (OperationCanceledException e) {
        // catch this exception so as to not enter the catch(RuntimeException e) below
        // might need to do the same for AbortCompilation
        throw e;
      } catch (JavaModelException e) {
        // GRECLIPSE-1480 don't log element does not exist exceptions. since this could occur when
        // element is in a non-java
        // project
        if (e.getStatus().getCode() != IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST
            || this.getJavaProject().exists()) {
          Util.log(e, "Problem with build structure for " + this.getElementName()); // $NON-NLS-1$
        }
      } catch (Exception e) {
        // GROOVY: The groovy compiler does not handle broken code well in many situations
        // use this general catch clause so that exceptions thrown by broken code
        // do not bubble up the stack.
        Util.log(e, "Problem with build structure for " + this.getElementName()); // $NON-NLS-1$
      } finally {
        if (compilationUnitDeclaration != null) {
          compilationUnitDeclaration.cleanUp();
        }
      }
      return unitInfo.isStructureKnown();
    } finally {
      depth.set(depth.get() - 1);
      if (GroovyLogManager.manager.hasLoggers()) {
        GroovyLogManager.manager.logEnd(
            "Build structure: " + name + " : " + Thread.currentThread().getName(),
            TraceCategory.COMPILER);
      }
    }
  }