/** * <copyright> * </copyright> * * $Id$ */ package org.eclipse.buckminster.rmap.pde.impl; import org.eclipse.buckminster.model.common.Replace; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import org.eclipse.buckminster.model.common.CommonConstants; import org.eclipse.buckminster.model.common.CommonFactory; import org.eclipse.buckminster.model.common.ComponentIdentifier; import org.eclipse.buckminster.model.common.Format; import org.eclipse.buckminster.model.common.PropertyRef; import org.eclipse.buckminster.model.common.Value; import org.eclipse.buckminster.rmap.Locator; import org.eclipse.buckminster.rmap.Matcher; import org.eclipse.buckminster.rmap.Provider; import org.eclipse.buckminster.rmap.ResourceMap; import org.eclipse.buckminster.rmap.RmapFactory; import org.eclipse.buckminster.rmap.RmapPackage; import org.eclipse.buckminster.rmap.SearchPath; import org.eclipse.buckminster.rmap.VersionConverter; import org.eclipse.buckminster.rmap.impl.ProviderImpl; import org.eclipse.buckminster.rmap.pde.PDEMapProvider; import org.eclipse.buckminster.rmap.pde.PdePackage; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.NotificationChain; import org.eclipse.buckminster.rmap.pde.util.MapFileEntry; import org.eclipse.buckminster.rmap.util.ICatalogReader; import org.eclipse.buckminster.rmap.util.IComponentReader; import org.eclipse.buckminster.rmap.util.RmapResourceFactoryImpl; import org.eclipse.buckminster.runtime.Buckminster; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.IOUtils; import org.eclipse.buckminster.runtime.MonitorUtils; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.impl.ENotificationImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.equinox.p2.metadata.Version; import static org.eclipse.buckminster.core.helpers.MapUtils.*; /** * <!-- begin-user-doc --> An implementation of the model object ' * <em><b>PDE Map Provider</b></em>'. <!-- end-user-doc --> * <p> * The following features are implemented: * <ul> * <li> * {@link org.eclipse.buckminster.rmap.pde.impl.PDEMapProviderImpl#getReplace * <em>Replace</em>}</li> * </ul> * </p> * * @generated */ public class PDEMapProviderImpl extends ProviderImpl implements PDEMapProvider { private static void collectEntries(File mapFile, Map<String, String> properties, Map<ComponentIdentifier, MapFileEntry> map) throws CoreException { InputStream input = null; try { input = new FileInputStream(mapFile); ArrayList<MapFileEntry> list = new ArrayList<MapFileEntry>(); org.eclipse.buckminster.rmap.pde.util.MapFile.parse(input, mapFile.getCanonicalPath(), properties, list); for (MapFileEntry entry : list) map.put(entry.getComponentIdentifier(), entry); } catch (IOException e) { throw BuckminsterException.wrap(e); } finally { IOUtils.close(input); } } private static Format convertFetchFactoryLocator(String readerType, Map<String, Object> fetchFactoryLocator, ComponentIdentifier componentName) throws CoreException { Format uri; if ("cvs".equals(readerType)) uri = convertFetchFactoryLocator_cvs(fetchFactoryLocator, componentName); else if ("url".equals(readerType)) uri = convertFetchFactoryLocator_url(fetchFactoryLocator, componentName); else if ("p2".equals(readerType)) uri = convertFetchFactoryLocator_p2(fetchFactoryLocator, componentName); else throw BuckminsterException.fromMessage("Reader %s cannot handle fetchFactory data", readerType); if (uri == null) throw BuckminsterException.fromMessage("Illegal fetch factory locator for reader %s", readerType); return uri; } private static Format convertFetchFactoryLocator_cvs(Map<String, Object> fetchFactoryLocator, ComponentIdentifier componentName) throws CoreException { String cvsRoot = getString(fetchFactoryLocator, "cvsRoot"); //$NON-NLS-1$ if (cvsRoot == null) return null; PropertyRef cnameRef = null; StringBuilder locator = new StringBuilder(cvsRoot); String pathStr = getString(fetchFactoryLocator, "path"); //$NON-NLS-1$ locator.append(','); if (pathStr != null) { IPath path = Path.fromPortableString(pathStr); String last = path.lastSegment(); String id = componentName.getId(); if (last.startsWith(id)) { if (path.segmentCount() > 1) { locator.append(path.removeLastSegments(1).toPortableString()); locator.append('/'); } locator.append("{0}"); if (last.length() > id.length()) locator.append(last.substring(id.length())); cnameRef = CommonFactory.eINSTANCE.createPropertyRef(); } else locator.append(pathStr); } else { locator.append("{0}"); cnameRef = CommonFactory.eINSTANCE.createPropertyRef(); } Format fmt = CommonFactory.eINSTANCE.createFormat(); fmt.setFormat(locator.toString()); if (cnameRef != null) { cnameRef.setKey(CommonConstants.COMPONENT_NAME); fmt.getValues().add(cnameRef); } return fmt; } private static Format convertFetchFactoryLocator_p2(Map<String, Object> fetchFactoryLocator, ComponentIdentifier componentName) throws CoreException { String repoURI = getString(fetchFactoryLocator, "repository"); //$NON-NLS-1$ if (repoURI == null) return null; Format fmt = CommonFactory.eINSTANCE.createFormat(); fmt.setFormat(repoURI); return fmt; } private static Format convertFetchFactoryLocator_url(Map<String, Object> fetchFactoryLocator, ComponentIdentifier componentName) throws CoreException { String repoURI = getString(fetchFactoryLocator, "src"); //$NON-NLS-1$ if (repoURI == null) return null; Format fmt = CommonFactory.eINSTANCE.createFormat(); fmt.setFormat(repoURI); return fmt; } private static Pattern createUniquePattern(String id, Map<String, Integer> idRefCounts) { StringBuilder bld = new StringBuilder(); bld.append('^'); int top = id.length(); for (int idx = 0; idx < top; ++idx) { char c = id.charAt(idx); switch (c) { case '.': bld.append('\\'); break; } bld.append(c); } bld.append('$'); String patternString = bld.toString(); Integer refCount = idRefCounts.get(patternString); if (refCount == null) refCount = Integer.valueOf(1); else refCount = Integer.valueOf(refCount.intValue() + 1); idRefCounts.put(patternString, refCount); return Pattern.compile(patternString); } /** * The cached value of the '{@link #getReplace() <em>Replace</em>}' * containment reference. <!-- begin-user-doc --> <!-- end-user-doc --> * * @see #getReplace() * @generated * @ordered */ protected Replace replace; /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ protected PDEMapProviderImpl() { super(); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ public NotificationChain basicSetReplace(Replace newReplace, NotificationChain msgs) { Replace oldReplace = replace; replace = newReplace; if (eNotificationRequired()) { ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, PdePackage.PDE_MAP_PROVIDER__REPLACE, oldReplace, newReplace); if (msgs == null) msgs = notification; else msgs.add(notification); } return msgs; } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ @Override public Object eGet(int featureID, boolean resolve, boolean coreType) { switch (featureID) { case PdePackage.PDE_MAP_PROVIDER__REPLACE: return getReplace(); } return super.eGet(featureID, resolve, coreType); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ @Override public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) { switch (featureID) { case PdePackage.PDE_MAP_PROVIDER__REPLACE: return basicSetReplace(null, msgs); } return super.eInverseRemove(otherEnd, featureID, msgs); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ @Override public boolean eIsSet(int featureID) { switch (featureID) { case PdePackage.PDE_MAP_PROVIDER__REPLACE: return replace != null; } return super.eIsSet(featureID); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ @Override public void eSet(int featureID, Object newValue) { switch (featureID) { case PdePackage.PDE_MAP_PROVIDER__REPLACE: setReplace((Replace) newValue); return; } super.eSet(featureID, newValue); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ @Override public void eUnset(int featureID) { switch (featureID) { case PdePackage.PDE_MAP_PROVIDER__REPLACE: setReplace((Replace) null); return; } super.eUnset(featureID); } @Override public ResourceMap getDelegationMap(IComponentReader reader, IStatus problemCollector, Map<ComponentIdentifier, Map<String, String>> queryHints, IProgressMonitor monitor) throws CoreException { Map<ComponentIdentifier, MapFileEntry> map = getMap(reader, problemCollector, monitor); if (map.isEmpty()) return null; int spIndex = 0; ResourceMap rmap = RmapFactory.eINSTANCE.createResourceMap(); Map<String, List<Provider>> providersPerReader = new HashMap<String, List<Provider>>(); Map<String, List<ComponentIdentifier>> idsPerSearchPath = new HashMap<String, List<ComponentIdentifier>>(); for (MapFileEntry entry : map.values()) { ComponentIdentifier ci = entry.getComponentIdentifier(); Map<String, Object> properties = entry.getProperties(); boolean source = true; Version v = null; String vs = null; String rt = entry.getReaderType(); boolean isP2 = "p2".equals(rt); if (isP2) { String vstr = getString(properties, "version"); //$NON-NLS-1$ if (vstr != null) { v = Version.parseVersion(vstr); addQueryHint(queryHints, ci, "version", v.toString()); } source = false; } else { String tag = getString(properties, "tag"); //$NON-NLS-1$ if (tag != null) vs = tag; VersionConverter vc = getVersionConverter(); if (vc != null) { // Let's check that the given tag matches what we are asking // for. // v = vc.createVersion(vs); addQueryHint(queryHints, ci, "version", v.toString()); } else { addQueryHint(queryHints, ci, "tag", tag); } if ("url".equals(rt)) { //$NON-NLS-1$ source = false; } } List<Provider> providers = providersPerReader.get(rt); if (providers == null) { providers = new ArrayList<Provider>(); providersPerReader.put(rt, providers); } Provider provider = null; Format providerURI = convertFetchFactoryLocator(rt, properties, ci); for (Provider candidate : providers) { if (candidate.getComponentTypes().get(0).equals(ci.getType()) && EcoreUtil.equals(candidate.getURI(), providerURI)) { provider = candidate; break; } } SearchPath searchPath; if (provider == null) { provider = RmapFactory.eINSTANCE.createProvider(); provider.setURI(providerURI); provider.setReaderType(rt); provider.getComponentTypes().add(ci.getType()); provider.setSource(source); providers.add(provider); searchPath = RmapFactory.eINSTANCE.createSearchPath(); searchPath.setName("sp" + spIndex++); searchPath.getProviders().add(provider); rmap.getSearchPaths().add(searchPath); } else searchPath = (SearchPath) provider.eContainer(); List<ComponentIdentifier> ids = idsPerSearchPath.get(searchPath.getName()); if (ids == null) { ids = new ArrayList<ComponentIdentifier>(); idsPerSearchPath.put(searchPath.getName(), ids); } ids.add(ci); } // Add locators for each search path, or if the search path is appointed // by only // one component id, add the provider directory to the rmap. Map<String, Integer> idRefCounts = new HashMap<String, Integer>(); List<Provider> singletonProviders = new ArrayList<Provider>(); Iterator<SearchPath> spIterator = rmap.getSearchPaths().iterator(); while (spIterator.hasNext()) { SearchPath sp = spIterator.next(); List<ComponentIdentifier> ids = idsPerSearchPath.get(sp.getName()); if (ids.size() == 1) { ComponentIdentifier ci = ids.get(0); Provider p = sp.getProviders().get(0); p.setPattern(createUniquePattern(ci.getId(), idRefCounts)); sp.getProviders().clear(); spIterator.remove(); Format fmt = p.getURI(); if (fmt.getValues().size() == 1) { // We might just as well resolve the component name here. // The provider will never be subject to other names Value v = fmt.getValues().get(0); if (v instanceof PropertyRef && CommonConstants.COMPONENT_NAME.equals(((PropertyRef) v).getKey())) { fmt.setFormat(fmt.getValue(Collections.singletonMap(CommonConstants.COMPONENT_NAME, ci.getId()))); fmt.getValues().clear(); } } singletonProviders.add(p); } else { for (ComponentIdentifier ci : ids) { Locator locator = RmapFactory.eINSTANCE.createLocator(); locator.setPattern(createUniquePattern(ci.getId(), idRefCounts)); locator.getComponentTypes().add(ci.getType()); locator.setSearchPath(sp); rmap.getMatchers().add(locator); } } } for (Provider p : singletonProviders) rmap.getMatchers().add(p); // Clear component type of Locators that are only referenced once. The // provider will // discriminate anyway. for (Matcher m : rmap.getMatchers()) { if (m instanceof Locator && idRefCounts.get(m.getPattern().toString()).intValue() == 1) m.getComponentTypes().clear(); } // Clean up the names spIndex = 0; spIterator = rmap.getSearchPaths().iterator(); while (spIterator.hasNext()) { SearchPath sp = spIterator.next(); sp.setName("sp" + spIndex++); } try { ResourceSet rs = new ResourceSetImpl(); rs.getResourceFactoryRegistry().getExtensionToFactoryMap().put("rmap", new RmapResourceFactoryImpl()); rs.getPackageRegistry().put(RmapPackage.eNS_URI, RmapPackage.eINSTANCE); Resource resource = rs.createResource(URI.createURI("http:///My.rmap")); resource.getContents().add(rmap); resource.save(System.out, null); } catch (IOException e) { } return rmap; } public Map<ComponentIdentifier, MapFileEntry> getMap(IComponentReader reader, IStatus problemCollector, IProgressMonitor monitor) throws CoreException { monitor.beginTask(null, 700); File tempFolder = null; try { Map<String, String> properties = reader.getProperties(); tempFolder = IOUtils.createTempFolder("bucky", ".tmp", IOUtils.getTempRoot(properties)); //$NON-NLS-1$ materializeMaps(tempFolder, reader, MonitorUtils.subMonitor(monitor, 500)); String[] mapFiles = tempFolder.list(); if (mapFiles == null || mapFiles.length == 0) return Collections.emptyMap(); MonitorUtils.worked(monitor, 50); Map<ComponentIdentifier, MapFileEntry> map = new HashMap<ComponentIdentifier, MapFileEntry>(); int amountPerFile = 100 / mapFiles.length; for (String file : mapFiles) { if (file.endsWith(".map")) //$NON-NLS-1$ collectEntries(new File(tempFolder, file), properties, map); MonitorUtils.worked(monitor, amountPerFile); } map = Collections.unmodifiableMap(map); return map; } catch (IOException e) { throw BuckminsterException.wrap(e); } finally { if (tempFolder != null) try { IOUtils.deleteRecursive(tempFolder, MonitorUtils.subMonitor(monitor, 50)); } catch (IOException e) { Buckminster.getLogger().warning(e, e.getMessage()); } monitor.done(); } } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ @Override public Replace getReplace() { return replace; } @Override public boolean hasDelegationMap() { return true; } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ @Override public void setReplace(Replace newReplace) { if (newReplace != replace) { NotificationChain msgs = null; if (replace != null) msgs = ((InternalEObject) replace).eInverseRemove(this, EOPPOSITE_FEATURE_BASE - PdePackage.PDE_MAP_PROVIDER__REPLACE, null, msgs); if (newReplace != null) msgs = ((InternalEObject) newReplace).eInverseAdd(this, EOPPOSITE_FEATURE_BASE - PdePackage.PDE_MAP_PROVIDER__REPLACE, null, msgs); msgs = basicSetReplace(newReplace, msgs); if (msgs != null) msgs.dispatch(); } else if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.SET, PdePackage.PDE_MAP_PROVIDER__REPLACE, newReplace, newReplace)); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ @Override protected EClass eStaticClass() { return PdePackage.Literals.PDE_MAP_PROVIDER; } private void addQueryHint(Map<ComponentIdentifier, Map<String, String>> queryHints, ComponentIdentifier ci, String key, String value) { Map<String, String> hints = queryHints.get(ci); if (hints == null) { hints = new HashMap<String, String>(); queryHints.put(ci, hints); } hints.put(key, value); } private void materializeMaps(File tempFolder, IComponentReader reader, IProgressMonitor monitor) throws CoreException { MonitorUtils.begin(monitor, 500); try { ((ICatalogReader) reader).materialize(new Path(tempFolder.toString()), MonitorUtils.subMonitor(monitor, 400)); } finally { IOUtils.close(reader); MonitorUtils.done(monitor); } } } // PDEMapProviderImpl