/******************************************************************************* * Copyright (c) 2004, 2006 * Thomas Hallgren, Kenneth Olwing, Mitch Sonies * Pontus Rydin, Nils Unden, Peer Torngren * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the individual * copyright holders listed above, as Initial Contributors under such license. * The text of such license is available at www.eclipse.org. *******************************************************************************/ package org.eclipse.buckminster.core.query.model; import static org.eclipse.buckminster.core.XMLConstants.BM_CQUERY_NS; import static org.eclipse.buckminster.core.XMLConstants.BM_CQUERY_PREFIX; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import org.eclipse.buckminster.core.CorePlugin; import org.eclipse.buckminster.core.Messages; import org.eclipse.buckminster.core.common.model.Documentation; import org.eclipse.buckminster.core.common.model.ExpandingProperties; import org.eclipse.buckminster.core.common.model.SAXEmitter; import org.eclipse.buckminster.core.cspec.model.ComponentName; import org.eclipse.buckminster.core.cspec.model.ComponentRequest; import org.eclipse.buckminster.core.helpers.BMProperties; import org.eclipse.buckminster.core.metadata.StorageManager; import org.eclipse.buckminster.core.metadata.model.IUUIDPersisted; import org.eclipse.buckminster.core.parser.IParser; import org.eclipse.buckminster.core.parser.IParserFactory; import org.eclipse.buckminster.core.query.IAdvisorNode; import org.eclipse.buckminster.core.query.IComponentQuery; import org.eclipse.buckminster.core.query.builder.AdvisorNodeBuilder; import org.eclipse.buckminster.core.query.builder.ComponentQueryBuilder; import org.eclipse.buckminster.core.rmap.model.ProviderScore; import org.eclipse.buckminster.core.version.VersionSelector; import org.eclipse.buckminster.download.DownloadManager; import org.eclipse.buckminster.osgi.filter.Filter; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.IOUtils; import org.eclipse.buckminster.runtime.Trivial; import org.eclipse.buckminster.runtime.URLUtils; import org.eclipse.buckminster.sax.UUIDKeyed; import org.eclipse.buckminster.sax.Utils; import org.eclipse.core.runtime.CoreException; import org.eclipse.ecf.core.security.IConnectContext; import org.eclipse.equinox.p2.metadata.VersionRange; import org.eclipse.osgi.util.NLS; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; /** * @author Thomas Hallgren */ public class ComponentQuery extends UUIDKeyed implements IUUIDPersisted, IComponentQuery { public static final String ATTR_PROPERTIES = "properties"; //$NON-NLS-1$ public static final String ATTR_RESOURCE_MAP = "resourceMap"; //$NON-NLS-1$ public static final String ATTR_SHORT_DESC = "shortDesc"; //$NON-NLS-1$ public static final String ELEM_ROOT_REQUEST = "rootRequest"; //$NON-NLS-1$ public static final int SEQUENCE_NUMBER = 4; public static final String TAG = "componentQuery"; //$NON-NLS-1$ public static ComponentQuery fromStream(URL url, IConnectContext cctx, InputStream stream, boolean validating) throws CoreException { try { IParserFactory pf = CorePlugin.getDefault().getParserFactory(); IParser<ComponentQuery> parser = pf.getComponentQueryParser(false); ComponentQuery cquery = parser.parse(url.toString(), stream); cquery.setConnectContext(cctx); return cquery; } catch (Exception e) { throw BuckminsterException.wrap(e); } } public static ComponentQuery fromURL(URL url, IConnectContext cctx, boolean validating) throws CoreException { InputStream stream = null; try { stream = DownloadManager.read(url, cctx); return fromStream(url, cctx, stream, validating); } catch (IOException e) { throw BuckminsterException.wrap(e); } finally { IOUtils.close(stream); } } private final List<AdvisorNode> advisorNodes; private transient Map<String, String> allProperties; private final Documentation documentation; private final Map<String, String> properties; private final URL contextURL; private final String propertiesURL; private final String resourceMapURL; private final ComponentRequest rootRequest; private final String shortDesc; private transient IConnectContext connectContext; public ComponentQuery(ComponentQueryBuilder bld) { documentation = bld.getDocumentation(); shortDesc = bld.getShortDesc(); propertiesURL = bld.getPropertiesURL(); resourceMapURL = bld.getResourceMapURL(); rootRequest = bld.getRootRequest(); List<AdvisorNodeBuilder> advisorNodeBuilders = bld.getAdvisoryNodes(); if (advisorNodeBuilders.size() == 0) advisorNodes = Collections.emptyList(); else { ArrayList<AdvisorNode> advisorNodeList = new ArrayList<AdvisorNode>(advisorNodeBuilders.size()); for (AdvisorNodeBuilder nodeBld : advisorNodeBuilders) advisorNodeList.add(nodeBld.create()); advisorNodes = Collections.unmodifiableList(advisorNodeList); } Map<String, String> props = bld.getDeclaredProperties(); if (props == null || props.size() == 0) properties = Collections.emptyMap(); else properties = Collections.unmodifiableMap(new ExpandingProperties<String>(props)); contextURL = bld.getContextURL(); } public boolean allowCircularDependency(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? false : node.allowCircularDependency(); } @Override public List<? extends IAdvisorNode> getAdvisoryNodes() { return advisorNodes; } public List<String> getAttributes(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? Collections.<String> emptyList() : node.getAttributes(); } public VersionSelector[] getBranchTagPath(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? VersionSelector.EMPTY_PATH : node.getBranchTagPath(); } public IConnectContext getConnectContext() { return connectContext; } @Override public URL getContextURL() { return contextURL; } @Override public Map<String, String> getDeclaredProperties() { return properties; } @Override public String getDefaultTag() { return TAG; } @Override public Documentation getDocumentation() { return documentation; } public ComponentRequest getExpandedRootRequest(Map<String, ? extends Object> props) { String name = rootRequest.getName(); String expName = ExpandingProperties.expand(props, name, 0); return name.equals(expName) ? rootRequest : new ComponentRequest(expName, rootRequest.getComponentTypeID(), rootRequest.getVersionRange()); } public synchronized Map<String, String> getGlobalProperties() { if (allProperties != null) return allProperties; allProperties = new ExpandingProperties<String>(); allProperties.putAll(properties); if (propertiesURL != null) { URL propsURL = getResolvedPropertiesURL(); InputStream input = null; try { input = DownloadManager.read(propsURL, getConnectContext()); Map<String, String> urlProps = new BMProperties(input); if (urlProps.size() > 0) { allProperties = new ExpandingProperties<String>(allProperties); allProperties.putAll(urlProps); } } catch (Exception e) { // We allow missing properties but we log it nevertheless // CorePlugin.getLogger().info(NLS.bind(Messages.Unable_to_read_property_file_0_1, propsURL, e.toString())); } finally { IOUtils.close(input); } } return allProperties; } public IAdvisorNode getMatchingNode(ComponentName cName, Map<String, ? extends Object> props) { String name = cName.getName(); for (IAdvisorNode aNode : advisorNodes) { Pattern pattern = aNode.getNamePattern(); if (!(pattern == null || pattern.matcher(name).find())) continue; String matchingType = aNode.getComponentTypeID(); if (!(matchingType == null || matchingType.equals(cName.getComponentTypeID()))) continue; Filter filter = aNode.getFilter(); if (filter == null || filter.matches(props)) return aNode; } return null; } @Override public AdvisorNode getNodeByCriteria(Pattern pattern, String componentType, Filter filter) { for (AdvisorNode node : advisorNodes) if (Trivial.equalsAllowNull(node.getNamePattern(), pattern) && Trivial.equalsAllowNull(node.getComponentTypeID(), componentType) && Trivial.equalsAllowNull(node.getFilter(), filter)) return node; return null; } public URL getOverlayFolder(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? null : node.getOverlayFolder(); } @Override public String getPropertiesURL() { return propertiesURL; } public ProviderScore getProviderScore(ComponentName cName, boolean mutable, boolean source, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); if (node == null) return ProviderScore.GOOD; ProviderScore mutableScore = ProviderScore.FAIR; switch (node.getMutableLevel()) { case REQUIRE: if (!mutable) return ProviderScore.REJECTED; mutableScore = ProviderScore.PREFERRED; break; case DESIRE: mutableScore = mutable ? ProviderScore.GOOD : ProviderScore.BAD; break; case AVOID: mutableScore = mutable ? ProviderScore.BAD : ProviderScore.GOOD; break; case REJECT: if (mutable) return ProviderScore.REJECTED; mutableScore = ProviderScore.PREFERRED; break; default: } ProviderScore sourceScore = ProviderScore.FAIR; switch (node.getSourceLevel()) { case REQUIRE: if (!source) return ProviderScore.REJECTED; sourceScore = ProviderScore.PREFERRED; break; case DESIRE: sourceScore = source ? ProviderScore.GOOD : ProviderScore.BAD; break; case AVOID: sourceScore = source ? ProviderScore.BAD : ProviderScore.GOOD; break; case REJECT: if (source) return ProviderScore.REJECTED; sourceScore = ProviderScore.PREFERRED; break; default: } return ProviderScore.values()[(sourceScore.ordinal() + mutableScore.ordinal()) / 2]; } public int[] getResolutionPrio(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? IAdvisorNode.DEFAULT_RESOLUTION_PRIO : node.getResolutionPrio(); } public URL getResolvedPropertiesURL() { return propertiesURL == null ? null : URLUtils.resolveURL(contextURL, ExpandingProperties.expand(BMProperties.getSystemProperties(), propertiesURL, 0)); } public URL getResolvedResourceMapURL() { return resourceMapURL == null ? null : URLUtils.resolveURL(contextURL, ExpandingProperties.expand(BMProperties.getSystemProperties(), resourceMapURL, 0)); } @Override public String getResourceMapURL() { return resourceMapURL; } public String getRevision(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? null : node.getRevision(); } @Override public ComponentRequest getRootRequest() { return rootRequest; } @Override public String getShortDesc() { return shortDesc; } public String getTagInfo() { return NLS.bind(Messages.Query_for_0, rootRequest); } public Date getTimestamp(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? null : node.getTimestamp(); } public VersionRange getVersionOverride(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? null : node.getVersionOverride(); } @Override public boolean isPersisted(StorageManager sm) throws CoreException { return false; } public boolean isPrune(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? false : node.isPrune(); } @Override public void remove(StorageManager sm) throws CoreException { throw new UnsupportedOperationException(); } public void removeAdvisorNode(IAdvisorNode node) { advisorNodes.remove(node); } public ComponentQuery resolve() { ComponentQueryBuilder bld = new ComponentQueryBuilder(); bld.initFrom(this); URL tmp = getResolvedPropertiesURL(); if (tmp != null) bld.setPropertiesURL(tmp.toString()); tmp = getResolvedResourceMapURL(); if (tmp != null) bld.setResourceMapURL(tmp.toString()); bld.setContextURL(null); return bld.createComponentQuery(); } public boolean skipComponent(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? false : node.skipComponent(); } @Override public void store(StorageManager sm) throws CoreException { throw new UnsupportedOperationException(); } @Override public void toSax(ContentHandler handler) throws SAXException { handler.startDocument(); toSax(handler, BM_CQUERY_NS, BM_CQUERY_PREFIX, getDefaultTag()); handler.endDocument(); } @Override public void toSax(ContentHandler handler, String namespace, String prefix, String localName) throws SAXException { handler.startPrefixMapping(BM_CQUERY_PREFIX, BM_CQUERY_NS); super.toSax(handler, namespace, prefix, localName); handler.endPrefixMapping(BM_CQUERY_PREFIX); } public boolean useMaterialization(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? true : node.isUseMaterialization(); } public boolean useResolutionService(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? true : node.isUseRemoteResolution(); } public boolean useTargetPlatform(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? true : node.isUseTargetPlatform(); } public boolean useWorkspace(ComponentName cName, Map<String, ? extends Object> props) { IAdvisorNode node = getMatchingNode(cName, props); return node == null ? true : node.isUseWorkspace(); } @Override protected void addAttributes(AttributesImpl attrs) throws SAXException { if (resourceMapURL != null) Utils.addAttribute(attrs, ATTR_RESOURCE_MAP, resourceMapURL.toString()); if (propertiesURL != null) Utils.addAttribute(attrs, ATTR_PROPERTIES, propertiesURL.toString()); if (shortDesc != null) Utils.addAttribute(attrs, ATTR_SHORT_DESC, shortDesc); } @Override protected void emitElements(ContentHandler handler, String namespace, String prefix) throws SAXException { if (documentation != null) documentation.toSax(handler, namespace, prefix, documentation.getDefaultTag()); rootRequest.toSax(handler, namespace, prefix, ELEM_ROOT_REQUEST); SAXEmitter.emitProperties(handler, properties, namespace, prefix, true, false); for (AdvisorNode node : advisorNodes) node.toSax(handler, namespace, prefix, node.getDefaultTag()); } @Override protected String getElementNamespace(String namespace) { return BM_CQUERY_NS; } @Override protected String getElementPrefix(String prefix) { return BM_CQUERY_PREFIX; } private void setConnectContext(IConnectContext cctx) { connectContext = cctx; } }