Java tutorial
/******************************************************************************* * Copyright (c) 2010, 2013 Oracle. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0, which accompanies this distribution * and is available at http://www.eclipse.org/legal/epl-v10.html. * * Contributors: * Oracle - initial API and implementation ******************************************************************************/ package org.eclipse.jpt.jaxb.core.internal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.Vector; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IResourceProxy; import org.eclipse.core.resources.IResourceProxyVisitor; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaElementDelta; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jpt.common.core.ContentTypeReference; import org.eclipse.jpt.common.core.JptResourceModel; import org.eclipse.jpt.common.core.JptResourceModelListener; import org.eclipse.jpt.common.core.internal.resource.java.binary.BinaryTypeCache; import org.eclipse.jpt.common.core.internal.resource.java.source.SourceTypeCompilationUnit; import org.eclipse.jpt.common.core.internal.utility.ContentTypeTools; import org.eclipse.jpt.common.core.internal.utility.ValidationMessageTools; import org.eclipse.jpt.common.core.resource.ProjectResourceLocator; import org.eclipse.jpt.common.core.resource.java.JavaResourceAbstractType; import org.eclipse.jpt.common.core.resource.java.JavaResourceAnnotatedElement; import org.eclipse.jpt.common.core.resource.java.JavaResourceCompilationUnit; import org.eclipse.jpt.common.core.resource.java.JavaResourceModel; import org.eclipse.jpt.common.core.resource.java.JavaResourcePackage; import org.eclipse.jpt.common.core.resource.java.JavaResourcePackageFragmentRoot; import org.eclipse.jpt.common.core.resource.java.JavaResourcePackageInfoCompilationUnit; import org.eclipse.jpt.common.core.resource.java.JavaResourceTypeCache; import org.eclipse.jpt.common.utility.command.Command; import org.eclipse.jpt.common.utility.command.ExtendedCommandContext; import org.eclipse.jpt.common.utility.internal.BitTools; import org.eclipse.jpt.common.utility.internal.command.ThreadLocalExtendedCommandContext; import org.eclipse.jpt.common.utility.internal.iterable.EmptyIterable; import org.eclipse.jpt.common.utility.internal.iterable.IterableTools; import org.eclipse.jpt.common.utility.internal.transformer.TransformerAdapter; import org.eclipse.jpt.jaxb.core.JaxbFile; import org.eclipse.jpt.jaxb.core.JaxbProject; import org.eclipse.jpt.jaxb.core.SchemaLibrary; import org.eclipse.jpt.jaxb.core.context.JaxbContextNode; import org.eclipse.jpt.jaxb.core.context.JaxbContextRoot; import org.eclipse.jpt.jaxb.core.context.JaxbPackage; import org.eclipse.jpt.jaxb.core.context.JaxbPackageInfo; import org.eclipse.jpt.jaxb.core.context.java.JavaType; import org.eclipse.jpt.jaxb.core.internal.platform.JaxbPlatformImpl; import org.eclipse.jpt.jaxb.core.internal.plugin.JptJaxbCorePlugin; import org.eclipse.jpt.jaxb.core.internal.utility.CallbackSynchronousSynchronizer; import org.eclipse.jpt.jaxb.core.internal.utility.SynchronousSynchronizer; import org.eclipse.jpt.jaxb.core.libprov.JaxbLibraryProviderInstallOperationConfig; import org.eclipse.jpt.jaxb.core.platform.JaxbPlatform; import org.eclipse.jpt.jaxb.core.resource.jaxbindex.JaxbIndexResource; import org.eclipse.jpt.jaxb.core.resource.jaxbprops.JaxbPropertiesResource; import org.eclipse.jpt.jaxb.core.utility.CallbackSynchronizer; import org.eclipse.jpt.jaxb.core.utility.Synchronizer; import org.eclipse.jpt.jaxb.core.validation.JptJaxbCoreValidationMessages; import org.eclipse.jst.common.project.facet.core.libprov.ILibraryProvider; import org.eclipse.jst.common.project.facet.core.libprov.LibraryInstallDelegate; import org.eclipse.jst.j2ee.model.internal.validation.ValidationCancelledException; import org.eclipse.wst.common.project.facet.core.IFacetedProject; import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion; import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager; import org.eclipse.wst.validation.internal.provisional.core.IMessage; import org.eclipse.wst.validation.internal.provisional.core.IReporter; /** * JAXB project. Holds all the JAXB stuff. * * The JAXB platform provides the hooks for vendor-specific stuff. * * The JAXB files are the "resource" model (i.e. objects that correspond directly * to Eclipse resources; e.g. Java source code files, XML files, JAR files). * * The root context node is the "context" model (i.e. objects that attempt to * model the JAXB spec, using the "resource" model as an adapter to the Eclipse * resources). */ public abstract class AbstractJaxbProject extends AbstractJaxbNode implements JaxbProject { /** * The Eclipse project corresponding to the JAXB project. */ protected final IProject project; /** * The vendor-specific JAXB platform that builds the JAXB project * and all its contents. */ protected final JaxbPlatform jaxbPlatform; /** * The library of schemas associated with this project */ protected final SchemaLibraryImpl schemaLibrary; /** * The JAXB files associated with the JAXB project: * java * jaxb.index * platform-specific files */ protected final Vector<JaxbFile> jaxbFiles = new Vector<JaxbFile>(); /** * The "external" Java resource compilation units (source). Populated upon demand. */ protected final Vector<JavaResourceCompilationUnit> externalJavaResourceCompilationUnits = new Vector<JavaResourceCompilationUnit>(); /** * The "external" Java resource persistent types (binary). Populated upon demand. */ protected final JavaResourceTypeCache externalJavaResourceTypeCache; /** * Resource models notify this listener when they change. A project update * should occur any time a resource model changes. */ protected final JptResourceModelListener resourceModelListener; /** * The root of the model representing the collated resources associated with * the JAXB project. */ protected final JaxbContextRoot contextRoot; /** * A pluggable synchronizer that keeps the JAXB * project's context model synchronized with its resource model, either * synchronously or asynchronously (or not at all). A synchronous synchronizer * is the default. For performance reasons, a UI should * immediately change this to an asynchronous synchronizer. A synchronous * synchronizer can be used when the project is being manipulated by a "batch" * (or non-UI) client (e.g. when testing "synchronization"). A null updater * can used during tests that do not care whether "synchronization" occur. * Clients will need to explicitly configure the synchronizer if they require * an asynchronous synchronizer. */ protected volatile Synchronizer contextModelSynchronizer; protected volatile boolean synchronizingContextModel = false; /** * A pluggable synchronizer that "updates" the JAXB project, either * synchronously or asynchronously (or not at all). A synchronous updater * is the default, allowing a newly-constructed JAXB project to be "updated" * upon return from the constructor. For performance reasons, a UI should * immediately change this to an asynchronous updater. A synchronous * updater can be used when the project is being manipulated by a "batch" * (or non-UI) client (e.g. when testing the "update" behavior). A null * updater can used during tests that do not care whether "synchronization" * occur. * Clients will need to explicitly configure the updater if they require * an asynchronous updater. */ protected volatile CallbackSynchronizer updateSynchronizer; protected final CallbackSynchronizer.Listener updateSynchronizerListener; /** * Support for modifying documents shared with the UI. */ protected final ThreadLocalExtendedCommandContext modifySharedDocumentCommandContext; // ********** constructor/initialization ********** protected AbstractJaxbProject(JaxbProject.Config config) { super(null); // JPA project is the root of the containment tree if ((config.getProject() == null) || (config.getPlatformDefinition() == null)) { throw new NullPointerException(); } this.project = config.getProject(); this.jaxbPlatform = new JaxbPlatformImpl(config.getPlatformDefinition()); this.schemaLibrary = new SchemaLibraryImpl(this); this.modifySharedDocumentCommandContext = this.buildModifySharedDocumentCommandContext(); this.resourceModelListener = this.buildResourceModelListener(); // build the JPA files corresponding to the Eclipse project's files InitialResourceProxyVisitor visitor = this.buildInitialResourceProxyVisitor(); visitor.visitProject(this.project); this.externalJavaResourceTypeCache = this.buildExternalJavaResourceTypeCache(); this.contextRoot = this.buildContextRoot(); // there *shouldn't* be any changes to the resource model... this.setContextModelSynchronizer_(this.buildSynchronousContextModelSynchronizer()); this.updateSynchronizerListener = this.buildUpdateSynchronizerListener(); // "update" the project before returning this.setUpdateSynchronizer_(this.buildSynchronousUpdateSynchronizer()); // start listening to this cache once the context model has been built // and all the external types are faulted in this.externalJavaResourceTypeCache.addResourceModelListener(this.resourceModelListener); } @Override protected boolean requiresParent() { return false; } @Override public IResource getResource() { return this.project; } protected ThreadLocalExtendedCommandContext buildModifySharedDocumentCommandContext() { return new ThreadLocalExtendedCommandContext(); } protected InitialResourceProxyVisitor buildInitialResourceProxyVisitor() { return new InitialResourceProxyVisitor(); } protected JavaResourceTypeCache buildExternalJavaResourceTypeCache() { return new BinaryTypeCache(this.jaxbPlatform.getAnnotationProvider()); } protected JaxbContextRoot buildContextRoot() { return this.getFactory().buildContextRoot(this); } // ***** inner class protected class InitialResourceProxyVisitor implements IResourceProxyVisitor { protected InitialResourceProxyVisitor() { super(); } protected void visitProject(IProject p) { try { p.accept(this, IResource.NONE); } catch (CoreException ex) { // shouldn't happen - we don't throw any CoreExceptions throw new RuntimeException(ex); } } // add a JPA file for every [appropriate] file encountered by the visitor public boolean visit(IResourceProxy resource) { switch (resource.getType()) { case IResource.ROOT: // shouldn't happen return true; // visit children case IResource.PROJECT: return true; // visit children case IResource.FOLDER: return true; // visit children case IResource.FILE: AbstractJaxbProject.this.addJaxbFile_((IFile) resource.requestResource()); return false; // no children default: return false; // no children } } } // ********** miscellaneous ********** /** * Ignore changes to this collection. Adds can be ignored since they are triggered * by requests that will, themselves, trigger updates (typically during the * update of an object that calls a setter with the newly-created resource * type). Deletes will be accompanied by manual updates. */ @Override protected void addNonUpdateAspectNamesTo(Set<String> nonUpdateAspectNames) { super.addNonUpdateAspectNamesTo(nonUpdateAspectNames); nonUpdateAspectNames.add(EXTERNAL_JAVA_RESOURCE_COMPILATION_UNITS_COLLECTION); } // ********** general queries ********** @Override public JaxbProject getJaxbProject() { return this; } public String getName() { return this.project.getName(); } @Override public void toString(StringBuilder sb) { sb.append(this.getName()); } public IProject getProject() { return this.project; } public IJavaProject getJavaProject() { return JavaCore.create(this.project); } @Override public JaxbPlatform getPlatform() { return this.jaxbPlatform; } public SchemaLibrary getSchemaLibrary() { return this.schemaLibrary; } @SuppressWarnings("unchecked") protected Iterable<JavaResourceCompilationUnit> getCombinedJavaResourceCompilationUnits() { return IterableTools.concatenate(this.getInternalJavaResourceCompilationUnits(), this.getExternalJavaResourceCompilationUnits()); } // ********** JAXB files ********** public Iterable<JaxbFile> getJaxbFiles() { return IterableTools.cloneLive(this.jaxbFiles); // read-only } public int getJaxbFilesSize() { return this.jaxbFiles.size(); } protected Iterable<JaxbFile> getJaxbFiles(final IContentType contentType) { return IterableTools.filter(this.getJaxbFiles(), new ContentTypeReference.ContentTypeIsKindOf(contentType)); } @Override public JaxbFile getJaxbFile(IFile file) { for (JaxbFile jaxbFile : this.getJaxbFiles()) { if (jaxbFile.getFile().equals(file)) { return jaxbFile; } } return null; } /** * Add a JAXB file for the specified file, if appropriate. * Return true if a JAXB File was created and added, false otherwise */ protected boolean addJaxbFile(IFile file) { JaxbFile jaxbFile = this.addJaxbFile_(file); if (jaxbFile != null) { this.fireItemAdded(JAXB_FILES_COLLECTION, jaxbFile); return true; } return false; } /** * Add a JAXB file for the specified file, if appropriate, without firing * an event; useful during construction. * Return the new JAXB file, null if it was not created. */ protected JaxbFile addJaxbFile_(IFile file) { if (isJavaFile(file)) { if (!getJavaProject().isOnClasspath(file)) { // a java (.jar or .java) file must be on the Java classpath return null; } } else if (!fileResourceLocationIsValid(file)) { return null; } JaxbFile jaxbFile = null; try { jaxbFile = this.getPlatform().buildJaxbFile(this, file); } catch (Exception e) { //log any developer exceptions and don't build a JaxbFile rather //than completely failing to build the JaxbProject JptJaxbCorePlugin.instance().logError(e); } if (jaxbFile == null) { return null; } jaxbFile.getResourceModel().addResourceModelListener(this.resourceModelListener); this.jaxbFiles.add(jaxbFile); return jaxbFile; } /* file is .java or .jar */ protected boolean isJavaFile(IFile file) { IContentType contentType = ContentTypeTools.contentType(file); return contentType != null && (contentType.isKindOf(JavaResourceCompilationUnit.CONTENT_TYPE) || contentType.isKindOf(JavaResourcePackageFragmentRoot.JAR_CONTENT_TYPE)); } /* (non-java resource) file is in acceptable resource location */ protected boolean fileResourceLocationIsValid(IFile file) { return this.getProjectResourceLocator().locationIsValid(file.getParent()); } protected ProjectResourceLocator getProjectResourceLocator() { return (ProjectResourceLocator) this.project.getAdapter(ProjectResourceLocator.class); } /** * Remove the JAXB file corresponding to the specified IFile, if it exists. * Return true if a JAXB File was removed, false otherwise */ protected boolean removeJaxbFile(IFile file) { JaxbFile jaxbFile = this.getJaxbFile(file); if (jaxbFile != null) { // a JpaFile is not added for every IFile this.removeJaxbFile(jaxbFile); return true; } return false; } /** * Stop listening to the JAXB file and remove it. */ protected void removeJaxbFile(JaxbFile jaxbFile) { jaxbFile.getResourceModel().removeResourceModelListener(this.resourceModelListener); if (!this.removeItemFromCollection(jaxbFile, this.jaxbFiles, JAXB_FILES_COLLECTION)) { throw new IllegalArgumentException(jaxbFile.toString()); } } // ********** external Java resource persistent types (source or binary) ********** protected JavaResourceAbstractType buildExternalJavaResourceType(String typeName) { //TODO typeName can be null in testing...need to look into this further. if (typeName != null) { IType jdtType = this.findType(typeName); return (jdtType == null) ? null : this.buildExternalJavaResourceType(jdtType); } return null; } /** * If the Java project has a class named <code>Foo</code> in the default package, * {@link IJavaProject#findType(String)} will return the {@link IType} * corresponding to <code>Foo</code> if the named passed to it is <code>".Foo"</code>. * This is not what we are expecting! So we had to put in a check for any * type name beginning with <code>'.'</code>. * See JDT bug 377710. */ protected IType findType(String typeName) { try { return typeName.startsWith(".") ? null : this.getJavaProject().findType(typeName); //$NON-NLS-1$ } catch (JavaModelException ex) { return null; // ignore exception? resource exception was probably already logged by JDT } } protected JavaResourceAbstractType buildExternalJavaResourceType(IType jdtType) { return jdtType.isBinary() ? this.buildBinaryExternalJavaResourceType(jdtType) : this.buildSourceExternalJavaResourceType(jdtType); } protected JavaResourceAbstractType buildBinaryExternalJavaResourceType(IType jdtType) { return this.externalJavaResourceTypeCache.addType(jdtType); } protected JavaResourceAbstractType buildSourceExternalJavaResourceType(IType jdtType) { JavaResourceCompilationUnit jrcu = this .getExternalJavaResourceCompilationUnit(jdtType.getCompilationUnit()); String jdtTypeName = jdtType.getFullyQualifiedName('.'); // JDT member type names use '$' for (JavaResourceAbstractType jrat : jrcu.getTypes()) { if (jrat.getTypeBinding().getQualifiedName().equals(jdtTypeName)) { return jrat; } } // we can get here if the project JRE is removed; // see SourceCompilationUnit#getPrimaryType(CompilationUnit) // bug 225332 return null; } // ********** external Java resource persistent types (binary) ********** public JavaResourceTypeCache getExternalJavaResourceTypeCache() { return this.externalJavaResourceTypeCache; } // ********** external Java resource compilation units (source) ********** public Iterable<JavaResourceCompilationUnit> getExternalJavaResourceCompilationUnits() { return IterableTools.cloneLive(this.externalJavaResourceCompilationUnits); // read-only } public int getExternalJavaResourceCompilationUnitsSize() { return this.externalJavaResourceCompilationUnits.size(); } /** * Return the resource model compilation unit corresponding to the specified * JDT compilation unit. If it does not exist, build it. */ protected JavaResourceCompilationUnit getExternalJavaResourceCompilationUnit( ICompilationUnit jdtCompilationUnit) { for (JavaResourceCompilationUnit jrcu : this.getExternalJavaResourceCompilationUnits()) { if (jrcu.getCompilationUnit().equals(jdtCompilationUnit)) { // we will get here if the JRCU could not build its persistent type... return jrcu; } } return this.addExternalJavaResourceCompilationUnit(jdtCompilationUnit); } /** * Add an external Java resource compilation unit. */ protected JavaResourceCompilationUnit addExternalJavaResourceCompilationUnit( ICompilationUnit jdtCompilationUnit) { JavaResourceCompilationUnit jrcu = this.buildJavaResourceCompilationUnit(jdtCompilationUnit); this.addItemToCollection(jrcu, this.externalJavaResourceCompilationUnits, EXTERNAL_JAVA_RESOURCE_COMPILATION_UNITS_COLLECTION); jrcu.addResourceModelListener(this.resourceModelListener); return jrcu; } protected JavaResourceCompilationUnit buildJavaResourceCompilationUnit(ICompilationUnit jdtCompilationUnit) { return new SourceTypeCompilationUnit(jdtCompilationUnit, this.jaxbPlatform.getAnnotationProvider(), this.jaxbPlatform.getAnnotationEditFormatter(), this.modifySharedDocumentCommandContext); } protected boolean removeExternalJavaResourceCompilationUnit(IFile file) { for (JavaResourceCompilationUnit jrcu : this.getExternalJavaResourceCompilationUnits()) { if (jrcu.getFile().equals(file)) { this.removeExternalJavaResourceCompilationUnit(jrcu); return true; } } return false; } protected void removeExternalJavaResourceCompilationUnit(JavaResourceCompilationUnit jrcu) { jrcu.removeResourceModelListener(this.resourceModelListener); this.removeItemFromCollection(jrcu, this.externalJavaResourceCompilationUnits, EXTERNAL_JAVA_RESOURCE_COMPILATION_UNITS_COLLECTION); } // ********** context model ********** public JaxbContextRoot getContextRoot() { return this.contextRoot; } public Iterable<? extends JaxbContextNode> getPrimaryJavaNodes(ICompilationUnit cu) { IFile file = getCorrespondingResource(cu); if (file == null) { return EmptyIterable.instance(); } IContentType contentType = ContentTypeTools.contentType(file); if (contentType == null) { return EmptyIterable.instance(); } if (contentType.isKindOf(JavaResourceCompilationUnit.PACKAGE_INFO_CONTENT_TYPE)) { try { return IterableTools.removeNulls(IterableTools.transform( IterableTools.iterable(cu.getPackageDeclarations()), new PackageDeclarationTransformer())); } catch (JavaModelException jme) { return EmptyIterable.instance(); } } else if (contentType.isKindOf(JavaResourceCompilationUnit.CONTENT_TYPE)) { try { return IterableTools.removeNulls( IterableTools.transform(IterableTools.iterable(cu.getAllTypes()), new TypeTransformer())); } catch (JavaModelException jme) { return EmptyIterable.instance(); } } return EmptyIterable.instance(); } class PackageDeclarationTransformer extends TransformerAdapter<IPackageDeclaration, JaxbPackageInfo> { @Override public JaxbPackageInfo transform(IPackageDeclaration o) { JaxbPackage jaxbPackage = getContextRoot().getPackage(o.getElementName()); return (jaxbPackage != null) ? jaxbPackage.getPackageInfo() : null; } } class TypeTransformer extends TransformerAdapter<IType, JavaType> { @Override public JavaType transform(IType o) { return getContextRoot().getJavaType(o.getFullyQualifiedName('.')); } } private IFile getCorrespondingResource(ICompilationUnit cu) { try { return (IFile) cu.getCorrespondingResource(); } catch (JavaModelException ex) { JptJaxbCorePlugin.instance().logError(ex); return null; } } // // ********** utility ********** // // public IFile getPlatformFile(IPath runtimePath) { // return JptCorePlugin.getPlatformFile(this.project, runtimePath); // } // // /** // * If the specified file exists, is significant to the JPA project, and its // * content is a "kind of" the specified content type, return the JPA // * resource model corresponding to the file; otherwise, return null. // */ // protected JpaResourceModel getResourceModel(IPath runtimePath, IContentType contentType) { // IFile file = this.getPlatformFile(runtimePath); // return file.exists() ? this.getResourceModel(file, contentType) : null; // } // // /** // * If the specified file is significant to the JPA project and its content // * is a "kind of" the specified content type, return the JPA resource model // * corresponding to the file; otherwise, return null. // */ // protected JpaResourceModel getResourceModel(IFile file, IContentType contentType) { // JpaFile jpaFile = this.getJpaFile(file); // return (jpaFile == null) ? null : jpaFile.getResourceModel(contentType); // } // ********** annotated Java source classes ********** public Iterable<JavaResourceAbstractType> getJavaSourceResourceTypes() { return IterableTools.children(this.getInternalJavaResourceCompilationUnits(), JavaResourceModel.Root.TYPES_TRANSFORMER); } public Iterable<JavaResourceAbstractType> getAnnotatedJavaSourceResourceTypes() { return IterableTools.filter(getJavaSourceResourceTypes(), JavaResourceAnnotatedElement.IS_ANNOTATED); } // public Iterable<String> getAnnotatedJavaSourceClassNames() { // return new TransformationIterable<JavaResourceType, String>(this.getInternalAnnotatedSourceJavaResourceTypes()) { // @Override // protected String transform(JavaResourceType type) { // return type.getQualifiedName(); // } // }; // } protected Iterable<JavaResourceCompilationUnit> getInternalJavaResourceCompilationUnits() { return IterableTools.downCast( IterableTools.transform(this.getJavaSourceJaxbFiles(), JaxbFile.RESOURCE_MODEL_TRANSFORMER)); } /** * return JAXB files with Java source "content" */ protected Iterable<JaxbFile> getJavaSourceJaxbFiles() { return this.getJaxbFiles(JavaResourceCompilationUnit.CONTENT_TYPE); } // ********** Java resource package look-up ********** public Iterable<JavaResourcePackage> getJavaResourcePackages() { return IterableTools.removeNulls( IterableTools.transform(this.getPackageInfoSourceJaxbFiles(), new JaxbFileTransformer())); } static class JaxbFileTransformer extends TransformerAdapter<JaxbFile, JavaResourcePackage> { @Override public JavaResourcePackage transform(JaxbFile jaxbFile) { return ((JavaResourcePackageInfoCompilationUnit) jaxbFile.getResourceModel()).getPackage(); } } public JavaResourcePackage getJavaResourcePackage(String packageName) { for (JavaResourcePackage jrp : this.getJavaResourcePackages()) { if (jrp.getName().equals(packageName)) { return jrp; } } return null; } public Iterable<JavaResourcePackage> getAnnotatedJavaResourcePackages() { return IterableTools.filter(this.getJavaResourcePackages(), JavaResourceAnnotatedElement.IS_ANNOTATED); } public JavaResourcePackage getAnnotatedJavaResourcePackage(String packageName) { JavaResourcePackage jrp = getJavaResourcePackage(packageName); return (jrp != null && jrp.isAnnotated()) ? jrp : null; } /** * return JPA files with package-info source "content" */ protected Iterable<JaxbFile> getPackageInfoSourceJaxbFiles() { return this.getJaxbFiles(JavaResourceCompilationUnit.PACKAGE_INFO_CONTENT_TYPE); } // ********** Java resource type look-up ********** public JavaResourceAbstractType getJavaResourceType(String typeName) { for (JavaResourceAbstractType type : this.getJavaResourceTypes()) { if (type.getTypeBinding().getQualifiedName().equals(typeName)) { return type; } } // if we don't have a type already, try to build new one from the project classpath return this.buildExternalJavaResourceType(typeName); } public JavaResourceAbstractType getJavaResourceType(String typeName, JavaResourceAbstractType.AstNodeType astNodeType) { JavaResourceAbstractType resourceType = getJavaResourceType(typeName); if (resourceType == null || resourceType.getAstNodeType() != astNodeType) { return null; } return resourceType; } protected Iterable<JavaResourceAbstractType> getJavaResourceTypes() { return IterableTools.children(this.getJavaResourceModelRoots(), JavaResourceModel.Root.TYPES_TRANSFORMER); } @SuppressWarnings("unchecked") protected Iterable<JavaResourceModel.Root> getJavaResourceModelRoots() { return IterableTools.concatenate(this.getInternalJavaResourceCompilationUnits(), // this.getInternalJavaResourcePackageFragmentRoots(), this.getExternalJavaResourceCompilationUnits(), Collections.singleton(this.externalJavaResourceTypeCache)); } // // ********** JARs ********** // // // TODO need to dermine if "listed" jar files have a place in JAXB tooling // public JavaResourcePackageFragmentRoot getJavaResourcePackageFragmentRoot(String jarFileName) { //// return this.getJarResourcePackageFragmentRoot(this.convertToPlatformFile(jarFileName)); // return this.getJavaResourcePackageFragmentRoot(this.getProject().getFile(jarFileName)); // } // // protected JavaResourcePackageFragmentRoot getJavaResourcePackageFragmentRoot(IFile jarFile) { // for (JavaResourcePackageFragmentRoot pfr : this.getInternalJavaResourcePackageFragmentRoots()) { // if (pfr.getFile().equals(jarFile)) { // return pfr; // } // } // return null; // } // // protected Iterable<JavaResourcePackageFragmentRoot> getInternalJavaResourcePackageFragmentRoots() { // return new TransformationIterable<JpaFile, JavaResourcePackageFragmentRoot>(this.getJarJpaFiles()) { // @Override // protected JavaResourcePackageFragmentRoot transform(JpaFile jpaFile) { // return (JavaResourcePackageFragmentRoot) jpaFile.getResourceModel(); // } // }; // } // // /** // * return JPA files with JAR "content" // */ // protected Iterable<JpaFile> getJarJpaFiles() { // return this.getJpaFiles(JptCorePlugin.JAR_CONTENT_TYPE); // } // // // ********** Java source folder names ********** // // public Iterable<String> getJavaSourceFolderNames() { // try { // return this.getJavaSourceFolderNames_(); // } catch (JavaModelException ex) { // JptCorePlugin.log(ex); // return EmptyIterable.instance(); // } // } // // protected Iterable<String> getJavaSourceFolderNames_() throws JavaModelException { // return new TransformationIterable<IPackageFragmentRoot, String>(this.getJavaSourceFolders()) { // @Override // protected String transform(IPackageFragmentRoot pfr) { // try { // return this.transform_(pfr); // } catch (JavaModelException ex) { // return "Error: " + pfr.getPath(); //$NON-NLS-1$ // } // } // private String transform_(IPackageFragmentRoot pfr) throws JavaModelException { // return pfr.getUnderlyingResource().getProjectRelativePath().toString(); // } // }; // } // // protected Iterable<IPackageFragmentRoot> getJavaSourceFolders() throws JavaModelException { // return new FilteringIterable<IPackageFragmentRoot>( // this.getPackageFragmentRoots(), // SOURCE_PACKAGE_FRAGMENT_ROOT_FILTER // ); // } // // protected static final Filter<IPackageFragmentRoot> SOURCE_PACKAGE_FRAGMENT_ROOT_FILTER = // new Filter<IPackageFragmentRoot>() { // public boolean accept(IPackageFragmentRoot pfr) { // try { // return this.accept_(pfr); // } catch (JavaModelException ex) { // return false; // } // } // private boolean accept_(IPackageFragmentRoot pfr) throws JavaModelException { // return pfr.exists() && (pfr.getKind() == IPackageFragmentRoot.K_SOURCE); // } // }; // // protected Iterable<IPackageFragmentRoot> getPackageFragmentRoots() throws JavaModelException { // return new ArrayIterable<IPackageFragmentRoot>(this.getJavaProject().getPackageFragmentRoots()); // } // **************** jaxb.index resources ********************************** public Iterable<JaxbIndexResource> getJaxbIndexResources() { return IterableTools.downCast(IterableTools.transform(getJaxbFiles(JaxbIndexResource.CONTENT_TYPE), JaxbFile.RESOURCE_MODEL_TRANSFORMER)); } public JaxbIndexResource getJaxbIndexResource(String packageName) { for (JaxbIndexResource jir : getJaxbIndexResources()) { if (packageName.equals(jir.getPackageName())) { return jir; } } return null; } // **************** jaxb.properties resources ***************************** public Iterable<JaxbPropertiesResource> getJaxbPropertiesResources() { return IterableTools.downCast(IterableTools.transform(getJaxbFiles(JaxbPropertiesResource.CONTENT_TYPE), JaxbFile.RESOURCE_MODEL_TRANSFORMER)); } public JaxbPropertiesResource getJaxbPropertiesResource(String packageName) { for (JaxbPropertiesResource jpr : getJaxbPropertiesResources()) { if (packageName.equals(jpr.getPackageName())) { return jpr; } } return null; } // ********** Java events ********** // TODO handle changes to external projects public void javaElementChanged(ElementChangedEvent event) { this.processJavaDelta(event.getDelta()); } /** * We recurse back here from {@link #processJavaDeltaChildren(IJavaElementDelta)}. */ protected void processJavaDelta(IJavaElementDelta delta) { switch (delta.getElement().getElementType()) { case IJavaElement.JAVA_MODEL: this.processJavaModelDelta(delta); break; case IJavaElement.JAVA_PROJECT: this.processJavaProjectDelta(delta); break; case IJavaElement.PACKAGE_FRAGMENT_ROOT: this.processJavaPackageFragmentRootDelta(delta); break; case IJavaElement.PACKAGE_FRAGMENT: this.processJavaPackageFragmentDelta(delta); break; case IJavaElement.COMPILATION_UNIT: this.processJavaCompilationUnitDelta(delta); break; default: break; // ignore the elements inside a compilation unit } } protected void processJavaDeltaChildren(IJavaElementDelta delta) { for (IJavaElementDelta child : delta.getAffectedChildren()) { this.processJavaDelta(child); // recurse } } /** * Return whether the specified Java element delta is for a * {@link IJavaElementDelta#CHANGED CHANGED} * (as opposed to {@link IJavaElementDelta#ADDED ADDED} or * {@link IJavaElementDelta#REMOVED REMOVED}) Java element * and the specified flag is set. * (The delta flags are only significant if the delta * {@link IJavaElementDelta#getKind() kind} is * {@link IJavaElementDelta#CHANGED CHANGED}.) */ protected boolean deltaFlagIsSet(IJavaElementDelta delta, int flag) { return (delta.getKind() == IJavaElementDelta.CHANGED) && BitTools.flagIsSet(delta.getFlags(), flag); } // ***** model protected void processJavaModelDelta(IJavaElementDelta delta) { // process the Java model's projects this.processJavaDeltaChildren(delta); } // ***** project protected void processJavaProjectDelta(IJavaElementDelta delta) { // process the Java project's package fragment roots this.processJavaDeltaChildren(delta); // a classpath change can have pretty far-reaching effects... if (this.classpathHasChanged(delta)) { this.rebuild((IJavaProject) delta.getElement()); } } /** * The specified Java project's classpath changed. Rebuild the JPA project * as appropriate. */ protected void rebuild(IJavaProject javaProject) { // if the classpath has changed, we need to update everything since // class references could now be resolved (or not) etc. if (javaProject.equals(this.getJavaProject())) { this.removeDeadJpaFiles(); this.synchronizeWithJavaSource(this.getInternalJavaResourceCompilationUnits()); } else { // TODO see if changed project is on our classpath? this.synchronizeWithJavaSource(this.getExternalJavaResourceCompilationUnits()); } } /** * Loop through all our JPA files, remove any that are no longer on the * classpath. */ protected void removeDeadJpaFiles() { for (JaxbFile jaxbFile : this.getJaxbFiles()) { if (this.jaxbFileIsDead(jaxbFile)) { this.removeJaxbFile(jaxbFile); } } } protected boolean jaxbFileIsDead(JaxbFile jaxbFile) { return !this.jaxbFileIsAlive(jaxbFile); } /** * Sometimes (e.g. during tests), when a project has been deleted, we get a * Java change event that indicates the Java project is CHANGED (as * opposed to REMOVED, which is what typically happens). The event's delta * indicates that everything in the Java project has been deleted and the * classpath has changed. All entries in the classpath have been removed; * but single entry for the Java project's root folder has been added. (!) * This means any file in the project is on the Java project's classpath. * This classpath change is what triggers us to rebuild the JPA project; so * we put an extra check here to make sure the JPA file's resource file is * still present. * <p> * This would not be a problem if Dali received the resource change event * <em>before</em> JDT and simply removed the JPA project; but JDT receives * the resource change event first and converts it into the problematic * Java change event.... */ protected boolean jaxbFileIsAlive(JaxbFile jaxbFile) { IFile file = jaxbFile.getFile(); return this.getJavaProject().isOnClasspath(file) && file.exists(); } /** * pre-condition: * delta.getElement().getElementType() == IJavaElement.JAVA_PROJECT */ protected boolean classpathHasChanged(IJavaElementDelta delta) { return this.deltaFlagIsSet(delta, IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED); } protected void synchronizeWithJavaSource(Iterable<JavaResourceCompilationUnit> javaResourceCompilationUnits) { for (JavaResourceCompilationUnit javaResourceCompilationUnit : javaResourceCompilationUnits) { javaResourceCompilationUnit.synchronizeWithJavaSource(); } } // ***** package fragment root protected void processJavaPackageFragmentRootDelta(IJavaElementDelta delta) { // process the Java package fragment root's package fragments this.processJavaDeltaChildren(delta); if (this.classpathEntryHasBeenAdded(delta)) { // TODO bug 277218 } else if (this.classpathEntryHasBeenRemoved(delta)) { // should be mutually-exclusive w/added (?) // TODO bug 277218 } } /** * pre-condition: * delta.getElement().getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT */ protected boolean classpathEntryHasBeenAdded(IJavaElementDelta delta) { return this.deltaFlagIsSet(delta, IJavaElementDelta.F_ADDED_TO_CLASSPATH); } /** * pre-condition: * delta.getElement().getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT */ protected boolean classpathEntryHasBeenRemoved(IJavaElementDelta delta) { return this.deltaFlagIsSet(delta, IJavaElementDelta.F_REMOVED_FROM_CLASSPATH); } // ***** package fragment protected void processJavaPackageFragmentDelta(IJavaElementDelta delta) { // process the java package fragment's compilation units this.processJavaDeltaChildren(delta); } // ***** compilation unit protected void processJavaCompilationUnitDelta(IJavaElementDelta delta) { if (this.javaCompilationUnitDeltaIsRelevant(delta)) { ICompilationUnit compilationUnit = (ICompilationUnit) delta.getElement(); for (JavaResourceCompilationUnit jrcu : this.getCombinedJavaResourceCompilationUnits()) { if (jrcu.getCompilationUnit().equals(compilationUnit)) { jrcu.synchronizeWithJavaSource(); // TODO ? this.resolveJavaTypes(); // might have new member types now... break; // there *shouldn't* be any more... } } } // ignore the java compilation unit's children } protected boolean javaCompilationUnitDeltaIsRelevant(IJavaElementDelta delta) { // ignore changes to/from primary working copy - no content has changed; // and make sure there are no other flags set that indicate *both* a // change to/from primary working copy *and* content has changed if (BitTools.onlyFlagIsSet(delta.getFlags(), IJavaElementDelta.F_PRIMARY_WORKING_COPY)) { return false; } // ignore java notification for ADDED or REMOVED; // these are handled via resource notification return delta.getKind() == IJavaElementDelta.CHANGED; } // ********** validation ********** public Iterable<IMessage> getValidationMessages(IReporter reporter) { List<IMessage> messages = new ArrayList<IMessage>(); this.validate(messages, reporter); return IterableTools.cloneSnapshot(messages); } protected void validate(List<IMessage> messages, IReporter reporter) { if (reporter.isCancelled()) { throw new ValidationCancelledException(); } getSchemaLibrary().refreshAllSchemas(); validateLibraryProvider(messages); validateSchemaLibrary(messages); this.contextRoot.validate(messages, reporter); } protected void validateLibraryProvider(List<IMessage> messages) { IFacetedProject facetedProject = null; try { facetedProject = ProjectFacetsManager.create(getProject()); } catch (CoreException ce) { JptJaxbCorePlugin.instance().logError(ce); return; } IProjectFacetVersion facetVersion = facetedProject.getInstalledVersion(JaxbProject.FACET); LibraryInstallDelegate lid = new LibraryInstallDelegate(facetedProject, facetVersion); ILibraryProvider lp = lid.getLibraryProvider(); if (lid.getLibraryProviderOperationConfig() instanceof JaxbLibraryProviderInstallOperationConfig) { ((JaxbLibraryProviderInstallOperationConfig) lid.getLibraryProviderOperationConfig()) .setJaxbPlatformConfig(getPlatform().getConfig()); } if (!lp.isEnabledFor(facetedProject, facetVersion) || !lid.validate().isOK()) { messages.add(ValidationMessageTools.buildValidationMessage(this.project, JptJaxbCoreValidationMessages.PROJECT_INVALID_LIBRARY_PROVIDER)); } lid.dispose(); } protected void validateSchemaLibrary(List<IMessage> messages) { this.schemaLibrary.validate(messages); } // ********** dispose ********** public void dispose() { this.contextModelSynchronizer.stop(); this.updateSynchronizer.stop(); this.updateSynchronizer.removeListener(this.updateSynchronizerListener); this.schemaLibrary.dispose(); // the XML resources are held indefinitely by the WTP translator framework, // so we better remove our listener or the JAXB project will not be GCed for (JaxbFile jaxbFile : this.getJaxbFiles()) { jaxbFile.getResourceModel().removeResourceModelListener(this.resourceModelListener); } } // ********** resource model listener ********** protected JptResourceModelListener buildResourceModelListener() { return new DefaultResourceModelListener(); } protected class DefaultResourceModelListener implements JptResourceModelListener { protected DefaultResourceModelListener() { super(); } public void resourceModelChanged(JptResourceModel jpaResourceModel) { // String msg = Thread.currentThread() + " resource model change: " + jpaResourceModel; // System.out.println(msg); // new Exception(msg).printStackTrace(System.out); AbstractJaxbProject.this.synchronizeContextModel(jpaResourceModel); } public void resourceModelReverted(JptResourceModel jpaResourceModel) { // IFile file = WorkbenchResourceHelper.getFile((JptXmlResource)jpaResourceModel); // AbstractJaxbProject.this.removeJaxbFile(file); // AbstractJaxbProject.this.addJaxbFile(file); } public void resourceModelUnloaded(JptResourceModel jpaResourceModel) { // IFile file = WorkbenchResourceHelper.getFile((JptXmlResource)jpaResourceModel); // AbstractJaxbProject.this.removeJaxbFile(file); } } protected void synchronizeContextModel(@SuppressWarnings("unused") JptResourceModel jpaResourceModel) { this.synchronizeContextModel(); } // ********** resource events ********** // TODO need to do the same thing for external projects and compilation units public void projectChanged(IResourceDelta delta) { if (delta.getResource().equals(this.getProject())) { this.internalProjectChanged(delta); } else { this.externalProjectChanged(delta); } } protected void internalProjectChanged(IResourceDelta delta) { ResourceDeltaVisitor resourceDeltaVisitor = this.buildInternalResourceDeltaVisitor(); resourceDeltaVisitor.visitDelta(delta); // at this point, if we have added and/or removed JpaFiles, an "update" will have been triggered; // any changes to the resource model during the "resolve" will trigger further "updates"; // there should be no need to "resolve" external Java types (they can't have references to // the internal Java types) if (resourceDeltaVisitor.encounteredSignificantChange()) { this.resolveInternalJavaTypes(); } } protected ResourceDeltaVisitor buildInternalResourceDeltaVisitor() { return new ResourceDeltaVisitor() { @Override public boolean fileChangeIsSignificant(IFile file, int deltaKind) { return AbstractJaxbProject.this.synchronizeJaxbFiles(file, deltaKind); } }; } /** * Internal resource delta visitor callback. * Return true if a JaxbFile was either added or removed. */ protected boolean synchronizeJaxbFiles(IFile file, int deltaKind) { switch (deltaKind) { case IResourceDelta.ADDED: return this.addJaxbFile(file); case IResourceDelta.REMOVED: return this.removeJaxbFile(file); case IResourceDelta.CHANGED: return this.checkForChangedFileContent(file); case IResourceDelta.ADDED_PHANTOM: break; // ignore case IResourceDelta.REMOVED_PHANTOM: break; // ignore default: break; // only worried about added/removed/changed files } return false; } protected boolean checkForChangedFileContent(IFile file) { JaxbFile jaxbFile = this.getJaxbFile(file); if (jaxbFile == null) { // the file might have changed its content to something that we are interested in return this.addJaxbFile(file); } if (jaxbFile.getContentType().equals(ContentTypeTools.contentType(file))) { // content has not changed - ignore return false; } // the content type changed, we need to build a new JPA file // (e.g. the schema of an orm.xml file changed from JPA to EclipseLink) this.removeJaxbFile(jaxbFile); this.addJaxbFile(file); return true; // at the least, we have removed a JPA file } protected void resolveInternalJavaTypes() { for (JavaResourceCompilationUnit jrcu : this.getInternalJavaResourceCompilationUnits()) { jrcu.resolveTypes(); } } protected void externalProjectChanged(IResourceDelta delta) { if (this.getJavaProject().isOnClasspath(delta.getResource())) { ResourceDeltaVisitor resourceDeltaVisitor = this.buildExternalResourceDeltaVisitor(); resourceDeltaVisitor.visitDelta(delta); // force an "update" here since adding and/or removing an external Java type // will only trigger an "update" if the "resolve" causes something in the resource model to change if (resourceDeltaVisitor.encounteredSignificantChange()) { this.update(); this.resolveExternalJavaTypes(); this.resolveInternalJavaTypes(); } } } protected ResourceDeltaVisitor buildExternalResourceDeltaVisitor() { return new ResourceDeltaVisitor() { @Override public boolean fileChangeIsSignificant(IFile file, int deltaKind) { return AbstractJaxbProject.this.synchronizeExternalFiles(file, deltaKind); } }; } /** * external resource delta visitor callback * Return true if an "external" Java resource compilation unit * was added or removed. */ protected boolean synchronizeExternalFiles(IFile file, int deltaKind) { switch (deltaKind) { case IResourceDelta.ADDED: return this.externalFileAdded(file); case IResourceDelta.REMOVED: return this.externalFileRemoved(file); case IResourceDelta.CHANGED: break; // ignore case IResourceDelta.ADDED_PHANTOM: break; // ignore case IResourceDelta.REMOVED_PHANTOM: break; // ignore default: break; // only worried about added/removed/changed files } return false; } protected boolean externalFileAdded(IFile file) { IContentType contentType = ContentTypeTools.contentType(file); if (contentType == null) { return false; } if (contentType.equals(JavaResourceCompilationUnit.CONTENT_TYPE)) { return true; } if (contentType.equals(JavaResourcePackageFragmentRoot.JAR_CONTENT_TYPE)) { return true; } return false; } protected boolean externalFileRemoved(IFile file) { IContentType contentType = ContentTypeTools.contentType(file); if (contentType == null) { return false; } if (contentType.equals(JavaResourceCompilationUnit.CONTENT_TYPE)) { return this.removeExternalJavaResourceCompilationUnit(file); } if (contentType.equals(JavaResourcePackageFragmentRoot.JAR_CONTENT_TYPE)) { return this.externalJavaResourceTypeCache.removeTypes(file); } return false; } protected void resolveExternalJavaTypes() { for (JavaResourceCompilationUnit jrcu : this.getExternalJavaResourceCompilationUnits()) { jrcu.resolveTypes(); } } // ***** resource delta visitors /** * add or remove a JPA file for every [appropriate] file encountered by the visitor */ protected abstract class ResourceDeltaVisitor implements IResourceDeltaVisitor { protected boolean encounteredSignificantChange = false; protected ResourceDeltaVisitor() { super(); } protected void visitDelta(IResourceDelta delta) { try { delta.accept(this); } catch (CoreException ex) { // shouldn't happen - we don't throw any CoreExceptions throw new RuntimeException(ex); } } public boolean visit(IResourceDelta delta) { IResource res = delta.getResource(); switch (res.getType()) { case IResource.ROOT: return true; // visit children case IResource.PROJECT: return true; // visit children case IResource.FOLDER: return true; // visit children case IResource.FILE: this.fileChanged((IFile) res, delta.getKind()); return false; // no children default: return false; // no children (probably shouldn't get here...) } } protected void fileChanged(IFile file, int deltaKind) { if (this.fileChangeIsSignificant(file, deltaKind)) { this.encounteredSignificantChange = true; } } protected abstract boolean fileChangeIsSignificant(IFile file, int deltaKind); /** * Return whether the visitor encountered some sort of "significant" * change while traversing the IResourceDelta * (e.g. a JPA file was added or removed). */ protected boolean encounteredSignificantChange() { return this.encounteredSignificantChange; } } // ********** support for modifying documents shared with the UI ********** public void setThreadLocalModifySharedDocumentCommandContext(ExtendedCommandContext commandContext) { this.modifySharedDocumentCommandContext.set(commandContext); } public ExtendedCommandContext getModifySharedDocumentCommandContext() { return this.modifySharedDocumentCommandContext; } // ********** synchronize context model with resource model ********** public Synchronizer getContextModelSynchronizer() { return this.contextModelSynchronizer; } public void setContextModelSynchronizer(Synchronizer synchronizer) { if (synchronizer == null) { throw new NullPointerException(); } this.contextModelSynchronizer.stop(); this.setContextModelSynchronizer_(synchronizer); } protected void setContextModelSynchronizer_(Synchronizer synchronizer) { this.contextModelSynchronizer = synchronizer; this.contextModelSynchronizer.start(); } /** * Delegate to the context model synchronizer so clients can configure how * synchronizations occur. */ public void synchronizeContextModel() { this.synchronizingContextModel = true; this.contextModelSynchronizer.synchronize(); this.synchronizingContextModel = false; // There are some changes to the resource model that will not change // the existing context model and trigger an update (e.g. adding an // @Entity annotation when the the JPA project is automatically // discovering annotated classes); so we explicitly execute an update // here to discover those changes. this.update(); } /** * Called by the context model synchronizer. */ public IStatus synchronizeContextModel(IProgressMonitor monitor) { this.contextRoot.synchronizeWithResourceModel(); return Status.OK_STATUS; } public void synchronizeContextModelAndWait() { Synchronizer temp = this.contextModelSynchronizer; this.setContextModelSynchronizer(this.buildSynchronousContextModelSynchronizer()); this.synchronizeContextModel(); this.setContextModelSynchronizer(temp); } // ********** default context model synchronizer (synchronous) ********** protected Synchronizer buildSynchronousContextModelSynchronizer() { return new SynchronousSynchronizer(this.buildSynchronousContextModelSynchronizerCommand()); } protected Command buildSynchronousContextModelSynchronizerCommand() { return new SynchronousContextModelSynchronizerCommand(); } protected class SynchronousContextModelSynchronizerCommand implements Command { public void execute() { AbstractJaxbProject.this.synchronizeContextModel(new NullProgressMonitor()); } } // ********** project "update" ********** public CallbackSynchronizer getUpdateSynchronizer() { return this.updateSynchronizer; } public void setUpdateSynchronizer(CallbackSynchronizer synchronizer) { if (synchronizer == null) { throw new NullPointerException(); } this.updateSynchronizer.stop(); this.updateSynchronizer.removeListener(this.updateSynchronizerListener); this.setUpdateSynchronizer_(synchronizer); } protected void setUpdateSynchronizer_(CallbackSynchronizer synchronizer) { this.updateSynchronizer = synchronizer; this.updateSynchronizer.addListener(this.updateSynchronizerListener); this.updateSynchronizer.start(); } @Override public void stateChanged() { super.stateChanged(); this.update(); } /** * The JAXB project's state has changed, "update" those parts of the * JAXB project that are dependent on other parts of the JAXB project. * <p> * Delegate to the update synchronizer so clients can configure how * updates occur. * <p> * Ignore any <em>updates</em> that occur while we are synchronizing * the context model with the resource model because we will <em>update</em> * the context model at the completion of the <em>sync</em>. This is really * only useful for synchronous <em>syncs</em> and <em>updates</em>; since * the job scheduling rules will prevent the <em>sync</em> and * <em>update</em> jobs from running concurrently. * * @see #updateAndWait() */ protected void update() { if (!this.synchronizingContextModel) { this.updateSynchronizer.synchronize(); } } /** * Called by the update synchronizer. */ public IStatus update(IProgressMonitor monitor) { this.contextRoot.update(); return Status.OK_STATUS; } /** * This is the callback used by the update synchronizer to notify the JAXB * project that the "update" has quiesced (i.e. the "update" has completed * and there are no outstanding requests for further "updates"). */ public void updateQuiesced() { //nothing yet } public void updateAndWait() { CallbackSynchronizer temp = this.updateSynchronizer; this.setUpdateSynchronizer(this.buildSynchronousUpdateSynchronizer()); this.update(); this.setUpdateSynchronizer(temp); } // ********** default update synchronizer (synchronous) ********** protected CallbackSynchronizer buildSynchronousUpdateSynchronizer() { return new CallbackSynchronousSynchronizer(this.buildSynchronousUpdateSynchronizerCommand()); } protected Command buildSynchronousUpdateSynchronizerCommand() { return new SynchronousUpdateSynchronizerCommand(); } protected class SynchronousUpdateSynchronizerCommand implements Command { public void execute() { AbstractJaxbProject.this.update(new NullProgressMonitor()); } } // ********** update synchronizer listener ********** protected CallbackSynchronizer.Listener buildUpdateSynchronizerListener() { return new UpdateSynchronizerListener(); } protected class UpdateSynchronizerListener implements CallbackSynchronizer.Listener { public void synchronizationQuiesced(CallbackSynchronizer synchronizer) { AbstractJaxbProject.this.updateQuiesced(); } } }