Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.felix.sigil.eclipse.internal.model.project; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import org.apache.felix.sigil.common.config.BldFactory; import org.apache.felix.sigil.common.config.IBldProject; import org.apache.felix.sigil.common.config.IRepositoryConfig; import org.apache.felix.sigil.common.model.AbstractCompoundModelElement; import org.apache.felix.sigil.common.model.ICapabilityModelElement; import org.apache.felix.sigil.common.model.IModelElement; import org.apache.felix.sigil.common.model.IModelWalker; import org.apache.felix.sigil.common.model.IRequirementModelElement; import org.apache.felix.sigil.common.model.ModelElementFactory; import org.apache.felix.sigil.common.model.eclipse.ISigilBundle; import org.apache.felix.sigil.common.model.osgi.IBundleModelElement; import org.apache.felix.sigil.common.model.osgi.IPackageExport; import org.apache.felix.sigil.common.model.osgi.IPackageImport; import org.apache.felix.sigil.common.model.osgi.IRequiredBundle; import org.apache.felix.sigil.common.repository.IRepositoryManager; import org.apache.felix.sigil.common.repository.IResolution; import org.apache.felix.sigil.common.repository.ResolutionConfig; import org.apache.felix.sigil.common.repository.ResolutionException; import org.apache.felix.sigil.eclipse.PathUtil; import org.apache.felix.sigil.eclipse.SigilCore; import org.apache.felix.sigil.eclipse.job.ThreadProgressMonitor; import org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel; import org.apache.felix.sigil.eclipse.model.util.JavaHelper; import org.apache.felix.sigil.eclipse.progress.ProgressAdapter; import org.apache.felix.sigil.eclipse.repository.ResolutionMonitorAdapter; import org.apache.felix.sigil.utils.GlobCompiler; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jdt.core.ClasspathContainerInitializer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IParent; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.osgi.framework.Version; /** * @author dave * */ public class SigilProject extends AbstractCompoundModelElement implements ISigilProjectModel { private static final long serialVersionUID = 1L; private IFile bldProjectFile; private IProject project; private IBldProject bldProject; private ISigilBundle bundle; private List<IRequirementModelElement> lastReqs = new LinkedList<IRequirementModelElement>(); private List<ICapabilityModelElement> lastCaps = new LinkedList<ICapabilityModelElement>(); public SigilProject() { super("Sigil Project"); } public SigilProject(IProject project) throws CoreException { this(); this.project = project; bldProjectFile = project.getFile(new Path(SigilCore.SIGIL_PROJECT_FILE)); } public void save(IProgressMonitor monitor) throws CoreException { save(monitor, true); } public void save(IProgressMonitor monitor, boolean rebuildDependencies) throws CoreException { SubMonitor progress = SubMonitor.convert(monitor, 1000); if (bldProjectFile.getLocation().toFile().exists()) { bldProjectFile.setContents(buildContents(), IFile.KEEP_HISTORY, progress.newChild(10)); } else { bldProjectFile.create(buildContents(), true /* force */, progress.newChild(5)); project.refreshLocal(IResource.DEPTH_ONE, progress.newChild(5)); } if (rebuildDependencies) { rebuildDependencies(progress.newChild(900)); } } public void rebuildDependencies(IProgressMonitor monitor) throws CoreException { SubMonitor progress = SubMonitor.convert(monitor, 1000); HashSet<ICapabilityModelElement> changes = new HashSet<ICapabilityModelElement>(lastCaps); LinkedList<IRequirementModelElement> reqs = new LinkedList<IRequirementModelElement>(); LinkedList<ICapabilityModelElement> caps = new LinkedList<ICapabilityModelElement>(); checkChanges(progress.newChild(100), reqs, caps); boolean reqsChanged; boolean capsChanged; synchronized (this) { reqsChanged = isRequirementsChanged(reqs); capsChanged = isCapabilitiesChanged(caps); } if (reqsChanged) { processRequirementsChanges(progress.newChild(600)); SigilCore.rebuild(this, progress.newChild(50)); } progress.setWorkRemaining(250); if (capsChanged) { changes.addAll(caps); SigilCore.rebuildBundleDependencies(this, changes, progress.newChild(250)); } } public void flushDependencyState() { synchronized (this) { lastReqs.clear(); } } private void processRequirementsChanges(IProgressMonitor monitor) throws CoreException { SubMonitor progress = SubMonitor.convert(monitor, 100); IRepositoryManager manager = getRepositoryManager(); ResolutionConfig config = new ResolutionConfig( ResolutionConfig.INCLUDE_OPTIONAL | ResolutionConfig.IGNORE_ERRORS); try { IResolution resolution = manager.getBundleResolver().resolve(this, config, new ResolutionMonitorAdapter(progress.newChild(20))); markProblems(resolution); // pull remote bundles from repositories to be added to classpath if (!resolution.isSynchronized()) { resolution.synchronize(new ProgressAdapter(progress.newChild(80))); } } catch (ResolutionException e) { throw SigilCore.newCoreException("Failed to resolve dependencies", e); } } private void markProblems(IResolution resolution) { try { getProject().deleteMarkers(SigilCore.MARKER_UNRESOLVED_DEPENDENCY, true, IResource.DEPTH_ONE); // Find missing imports Collection<IPackageImport> imports = getBundle().getBundleInfo().getImports(); for (IPackageImport pkgImport : imports) { if (resolution.getProvider(pkgImport) == null) { markMissingImport(pkgImport, getProject()); } } // Find missing required bundles Collection<IRequiredBundle> requiredBundles = getBundle().getBundleInfo().getRequiredBundles(); for (IRequiredBundle requiredBundle : requiredBundles) { if (resolution.getProvider(requiredBundle) == null) { markMissingRequiredBundle(requiredBundle, getProject()); } } } catch (CoreException e) { SigilCore.error("Failed to update problems", e); } } private void checkChanges(IProgressMonitor monitor, final List<IRequirementModelElement> reqs, final List<ICapabilityModelElement> caps) { visit(new IModelWalker() { public boolean visit(IModelElement element) { if (element instanceof IRequirementModelElement) { reqs.add((IRequirementModelElement) element); } else if (element instanceof ICapabilityModelElement) { // also calculate uses during this pass to save multi pass on model if (element instanceof IPackageExport) { IPackageExport pe = (IPackageExport) element; try { pe.setUses(Arrays.asList(JavaHelper.findUses(pe.getPackageName(), SigilProject.this))); } catch (CoreException e) { SigilCore.error("Failed to build uses list for " + pe, e); } } caps.add((ICapabilityModelElement) element); } return true; } }); } private boolean isRequirementsChanged(List<IRequirementModelElement> dependencies) { if (lastReqs.equals(dependencies)) { return false; } else { lastReqs = dependencies; return true; } } private boolean isCapabilitiesChanged(List<ICapabilityModelElement> capabilites) { if (lastCaps.equals(capabilites)) { return false; } else { lastCaps = capabilites; return true; } } private static void markMissingImport(IPackageImport pkgImport, IProject project) throws CoreException { IMarker marker = project.getProject().createMarker(SigilCore.MARKER_UNRESOLVED_IMPORT_PACKAGE); marker.setAttribute("element", pkgImport.getPackageName()); marker.setAttribute("versionRange", pkgImport.getVersions().toString()); marker.setAttribute(IMarker.MESSAGE, "Cannot resolve imported package \"" + pkgImport.getPackageName() + "\" with version range " + pkgImport.getVersions()); marker.setAttribute(IMarker.SEVERITY, pkgImport.isOptional() ? IMarker.SEVERITY_WARNING : IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); } private static void markMissingRequiredBundle(IRequiredBundle req, IProject project) throws CoreException { IMarker marker = project.getProject().createMarker(SigilCore.MARKER_UNRESOLVED_REQUIRE_BUNDLE); marker.setAttribute("element", req.getSymbolicName()); marker.setAttribute("versionRange", req.getVersions().toString()); marker.setAttribute(IMarker.MESSAGE, "Cannot resolve required bundle \"" + req.getSymbolicName() + "\" with version range " + req.getVersions()); marker.setAttribute(IMarker.SEVERITY, req.isOptional() ? IMarker.SEVERITY_WARNING : IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); } public Collection<IClasspathEntry> findExternalClasspath(IProgressMonitor monitor) throws CoreException { return JavaHelper.resolveClasspathEntrys(this, monitor); } public Version getVersion() { ISigilBundle bundle = getBundle(); return bundle == null ? null : bundle.getBundleInfo() == null ? null : bundle.getBundleInfo().getVersion(); } public String getSymbolicName() { ISigilBundle bundle = getBundle(); return bundle == null ? null : bundle.getBundleInfo() == null ? null : bundle.getBundleInfo().getSymbolicName(); } public IProject getProject() { return project; } public ISigilBundle getBundle() { ISigilBundle b = null; try { synchronized (bldProjectFile) { if (bundle == null) { IPath loc = bldProjectFile.getLocation(); if (loc == null) { // callers can protect against this by using // checking exists() throw new IllegalStateException("Sigil project does not exist"); } else if (loc.toFile().exists()) { bundle = parseContents(bldProjectFile); } else { bundle = setupDefaults(); } } b = bundle; } } catch (CoreException e) { SigilCore.error("Failed to build bundle", e); } return b; } public void setBundle(ISigilBundle bundle) { synchronized (bldProjectFile) { this.bundle = bundle; } } public IJavaProject getJavaModel() { return JavaCore.create(project); } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; try { SigilProject p = (SigilProject) obj; return getSymbolicName().equals(p.getSymbolicName()) && (getVersion() == null ? p.getVersion() == null : getVersion().equals(p.getVersion())); } catch (ClassCastException e) { return false; } } @Override public int hashCode() { String bsn = getSymbolicName(); int hc = bsn == null ? 1 : bsn.hashCode(); if (getVersion() != null) { hc *= getVersion().hashCode(); } hc *= 7; return hc; } @Override public String toString() { return "SigilProject[" + getSymbolicName() + ":" + getVersion() + "]"; } public void resetClasspath(IProgressMonitor monitor, boolean forceResolve) throws CoreException { if (forceResolve) { processRequirementsChanges(monitor); } Path containerPath = new Path(SigilCore.CLASSPATH_CONTAINER_PATH); IJavaProject java = getJavaModel(); ClasspathContainerInitializer init = JavaCore .getClasspathContainerInitializer(SigilCore.CLASSPATH_CONTAINER_PATH); ThreadProgressMonitor.setProgressMonitor(monitor); try { init.requestClasspathContainerUpdate(containerPath, java, null); } finally { ThreadProgressMonitor.setProgressMonitor(null); } } public IPath findBundleLocation() throws CoreException { IPath p = PathUtil.newPathIfExists(getBundle().getLocation()); if (p == null) { p = SigilCore.getDefault().findDefaultBundleLocation(this); } return p; } public IModelElement findImport(final String packageName, final IProgressMonitor monitor) { final IModelElement[] found = new IModelElement[1]; visit(new IModelWalker() { public boolean visit(IModelElement element) { if (element instanceof IPackageImport) { IPackageImport pi = (IPackageImport) element; if (pi.getPackageName().equals(packageName)) { found[0] = pi; return false; } } else if (element instanceof IRequiredBundle) { IRequiredBundle rb = (IRequiredBundle) element; try { IRepositoryManager manager = SigilProject.this.getRepositoryManager(); ResolutionConfig config = new ResolutionConfig(ResolutionConfig.IGNORE_ERRORS); IResolution res = manager.getBundleResolver().resolve(rb, config, new ResolutionMonitorAdapter(monitor)); ISigilBundle b = res.getProvider(rb); for (IPackageExport pe : b.getBundleInfo().getExports()) { if (pe.getPackageName().equals(packageName)) { found[0] = rb; return false; } } } catch (ResolutionException e) { SigilCore.error("Failed to resolve " + rb, e); } } return true; } }); return found[0]; } public boolean isInClasspath(String packageName, IProgressMonitor monitor) throws CoreException { if (findImport(packageName, monitor) != null) { return true; } for (String path : getBundle().getClasspathEntrys()) { IClasspathEntry cp = getJavaModel().decodeClasspathEntry(path); for (IPackageFragmentRoot root : getJavaModel().findPackageFragmentRoots(cp)) { if (findPackage(packageName, root)) { return true; } } } return false; } public boolean isInClasspath(ISigilBundle bundle) { for (String path : getBundle().getClasspathEntrys()) { IClasspathEntry cp = getJavaModel().decodeClasspathEntry(path); switch (cp.getEntryKind()) { case IClasspathEntry.CPE_PROJECT: ISigilProjectModel p = bundle.getAncestor(ISigilProjectModel.class); return p != null && cp.getPath().equals(p.getProject().getFullPath()); case IClasspathEntry.CPE_LIBRARY: return cp.getPath().equals(bundle.getLocation()); } } return false; } private boolean findPackage(String packageName, IParent parent) throws JavaModelException { for (IJavaElement e : parent.getChildren()) { if (e.getElementType() == IJavaElement.PACKAGE_FRAGMENT) { return e.getElementName().equals(packageName); } if (e instanceof IParent) { if (findPackage(packageName, (IParent) e)) { return true; } } } return false; } private ISigilBundle setupDefaults() { ISigilBundle bundle = ModelElementFactory.getInstance().newModelElement(ISigilBundle.class); IBundleModelElement info = ModelElementFactory.getInstance().newModelElement(IBundleModelElement.class); bundle.setBundleInfo(info); bundle.setParent(this); return bundle; } private ISigilBundle parseContents(IFile projectFile) throws CoreException { if (projectFile.getName().equals(SigilCore.SIGIL_PROJECT_FILE)) { return parseBldContents(projectFile.getLocationURI()); } else { throw SigilCore.newCoreException("Unexpected project file: " + projectFile.getName(), null); } } private ISigilBundle parseBldContents(URI uri) throws CoreException { try { bldProject = BldFactory.getProject(uri, true); ISigilBundle bundle = bldProject.getDefaultBundle(); if (bundle == null) { throw SigilCore.newCoreException("No default bundle", null); } bundle.setParent(this); return bundle; } catch (IOException e) { throw SigilCore.newCoreException("Failed to parse " + uri, e); } } private InputStream buildContents() throws CoreException { ByteArrayOutputStream buf = new ByteArrayOutputStream(); try { if (bldProject == null) { bldProject = BldFactory.newProject(bldProjectFile.getLocationURI(), null); } bldProject.setDefaultBundle(getBundle()); bldProject.saveTo(buf); } catch (IOException e) { throw SigilCore.newCoreException("Failed to save project file", e); } return new ByteArrayInputStream(buf.toByteArray()); } public String getName() { return getProject().getName(); } public IPath findOutputLocation() throws CoreException { return getProject().getLocation().append(getJavaModel().getOutputLocation().removeFirstSegments(1)); } public IBldProject getBldProject() throws CoreException { try { return BldFactory.getProject(project.getFile(IBldProject.PROJECT_FILE).getLocationURI()); } catch (IOException e) { throw SigilCore.newCoreException("Failed to get project file: ", e); } } public IRepositoryConfig getRepositoryConfig() throws CoreException { try { return BldFactory.getConfig(project.getFile(IBldProject.PROJECT_FILE).getLocationURI()); } catch (IOException e) { throw SigilCore.newCoreException("Failed to get project file: ", e); } } public boolean isInBundleClasspath(IPackageFragment root) throws JavaModelException { if (getBundle().getClasspathEntrys().isEmpty()) { for (String p : getBundle().getPackages()) { SigilCore.log("Checking " + p + "->" + root.getElementName()); Matcher m = GlobCompiler.compile(p).matcher(root.getElementName()); if (m.matches()) { return true; } } return false; } else { IPackageFragmentRoot parent = (IPackageFragmentRoot) root.getParent(); String enc = getJavaModel().encodeClasspathEntry(parent.getRawClasspathEntry()); return getBundle().getClasspathEntrys().contains(enc.trim()); } } /* (non-Javadoc) * @see org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel#getRepositoryManager() */ public IRepositoryManager getRepositoryManager() { return SigilCore.getRepositoryManager(this); } /* (non-Javadoc) * @see org.apache.felix.sigil.eclipse.model.project.ISigilProjectModel#exists() */ public boolean exists() { return project.exists() && project.getFile(IBldProject.PROJECT_FILE).exists(); } }