| /******************************************************************************* |
| * Copyright (c) 2000, 2007 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.update.internal.search; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.update.core.ISite; |
| import org.eclipse.update.core.Utilities; |
| import org.eclipse.update.internal.configurator.UpdateURLDecoder; |
| import org.eclipse.update.internal.core.Messages; |
| import org.eclipse.update.internal.core.UpdateManagerUtils; |
| import org.eclipse.update.internal.core.connection.ConnectionFactory; |
| import org.eclipse.update.internal.core.connection.IResponse; |
| import org.eclipse.update.search.IUpdateSiteAdapter; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * |
| * This class opens connection to the update map resource |
| * and parses the file to load update URL mappings. |
| * These mappings are used to redirect new updates search |
| * when the redirection pattern matches. |
| */ |
| |
| public class UpdatePolicy { |
| private static final String TAG_POLICY = "update-policy"; //$NON-NLS-1$ |
| private static final String TAG_URL_MAP = "url-map"; //$NON-NLS-1$ |
| private static final String ATT_URL = "url"; //$NON-NLS-1$ |
| private static final String ATT_PATTERN = "pattern"; //$NON-NLS-1$ |
| private static final String ATT_TYPE = "url-type"; //$NON-NLS-1$ |
| private static final String ATT_TYPE_VALUE_UPDATE = "update"; //$NON-NLS-1$ |
| //private static final String ATT_TYPE_VALUE_BOTH = "both"; //$NON-NLS-1$ |
| private static final String ATT_TYPE_VALUE_DISCOVERY = "discovery"; //$NON-NLS-1$ |
| |
| private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); |
| |
| private static class MapSite implements IUpdateSiteAdapter { |
| private URL url; |
| public MapSite(URL url) { |
| this.url = url; |
| } |
| public String getLabel() { |
| if (url == null) { |
| return ""; //$NON-NLS-1$ |
| } |
| return url.toString(); |
| } |
| public URL getURL() { |
| return url; |
| } |
| } |
| |
| private static class UpdateMapEntry { |
| private IUpdateSiteAdapter site; |
| private String pattern; |
| |
| public UpdateMapEntry(String pattern, URL url) { |
| this.pattern = pattern; |
| this.site = new MapSite(url); |
| } |
| public IUpdateSiteAdapter getSite() { |
| return site; |
| } |
| public boolean matches(String id) { |
| return id.startsWith(pattern); |
| } |
| public String getPattern() { |
| return pattern; |
| } |
| } |
| |
| private ArrayList entries; |
| private ArrayList discoveryEntries; |
| private IUpdateSiteAdapter defaultSite; |
| private IUpdateSiteAdapter defaultDiscoverySite; |
| private boolean loaded = false; |
| private boolean fallbackAllowed = true; |
| |
| public UpdatePolicy() { |
| entries = new ArrayList(); |
| discoveryEntries = new ArrayList(); |
| } |
| |
| public void load(URL mapFile, IProgressMonitor monitor) |
| throws CoreException { |
| InputStream policyStream = null; |
| try { |
| IResponse response = ConnectionFactory.get(mapFile); |
| UpdateManagerUtils.checkConnectionResult(response, mapFile); |
| policyStream = response.getInputStream(monitor); |
| // the stream can be null if the user cancels the connection |
| if (policyStream == null) |
| return; |
| |
| documentBuilderFactory.setNamespaceAware(true); |
| DocumentBuilder parser = documentBuilderFactory.newDocumentBuilder(); |
| Document doc = parser.parse(new InputSource(policyStream)); |
| |
| processUpdatePolicy(doc); |
| loaded = true; |
| } catch (IOException e) { |
| throw Utilities.newCoreException( |
| NLS.bind(Messages.SiteURLFactory_UnableToAccessSiteStream, (new String[] { mapFile == null ? "" : mapFile.toExternalForm() })), //$NON-NLS-1$ |
| ISite.SITE_ACCESS_EXCEPTION, |
| e); |
| } catch (SAXException e) { |
| throw Utilities.newCoreException( |
| Messages.UpdatePolicy_parsePolicy, |
| 0, |
| e); |
| |
| } catch(ParserConfigurationException e) { |
| throw Utilities.newCoreException( |
| Messages.UpdatePolicy_parsePolicy, |
| 0, |
| e); |
| } finally { |
| if (policyStream != null) { |
| try { |
| policyStream.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| } |
| |
| public boolean isLoaded() { |
| return loaded; |
| } |
| |
| /* |
| * Given the feature ID, returns the mapped update URL if |
| * found in the mappings. This URL will be used INSTEAD of |
| * the update URL encoded in the feature itself during the |
| * new update search. |
| * <p>In case of multiple matches (e.g. org.eclipse and org.eclipse.platform) |
| * the URL for the longer pattern will be picked (i.e. org.eclipse.platform). |
| */ |
| public IUpdateSiteAdapter getMappedSite(String id) { |
| UpdateMapEntry lastEntry = null; |
| for (int i = 0; i < entries.size(); i++) { |
| UpdateMapEntry entry = (UpdateMapEntry) entries.get(i); |
| if (entry.matches(id)) { |
| if (lastEntry == null) |
| lastEntry = entry; |
| else { |
| // Choose the match with longer pattern. |
| // For example, if two matches are found: |
| // 'org.eclipse' and 'org.eclipse.platform', |
| // pick 'org.eclipse.platform'. |
| String pattern = entry.getPattern(); |
| String lastPattern = lastEntry.getPattern(); |
| if (pattern.length() > lastPattern.length()) |
| lastEntry = entry; |
| } |
| } |
| } |
| if (lastEntry != null) |
| return lastEntry.getSite(); |
| else |
| return defaultSite; |
| } |
| |
| /* |
| * Given the feature ID, returns the mapped discovery URL if |
| * found in the mappings. This URL will be used INSTEAD of |
| * the discovery URL encoded in the feature itself during the |
| * new search. |
| * <p>In case of multiple matches (e.g. org.eclipse and org.eclipse.platform) |
| * the URL for the longer pattern will be picked (i.e. org.eclipse.platform). |
| */ |
| public IUpdateSiteAdapter getMappedDiscoverySite(String id) { |
| UpdateMapEntry lastEntry = null; |
| for (int i = 0; i < discoveryEntries.size(); i++) { |
| UpdateMapEntry entry = (UpdateMapEntry) discoveryEntries.get(i); |
| if (entry.matches(id)) { |
| if (lastEntry == null) |
| lastEntry = entry; |
| else { |
| // Choose the match with longer pattern. |
| // For example, if two matches are found: |
| // 'org.eclipse' and 'org.eclipse.platform', |
| // pick 'org.eclipse.platform'. |
| String pattern = entry.getPattern(); |
| String lastPattern = lastEntry.getPattern(); |
| if (pattern.length() > lastPattern.length()) |
| lastEntry = entry; |
| } |
| } |
| } |
| if (lastEntry != null) |
| return lastEntry.getSite(); |
| else |
| return defaultDiscoverySite; |
| } |
| |
| public boolean isFallbackAllowed() { |
| return fallbackAllowed; |
| } |
| |
| private void reset() { |
| if (!entries.isEmpty()) |
| entries.clear(); |
| if (!discoveryEntries.isEmpty()) |
| discoveryEntries.clear(); |
| } |
| |
| private void processUpdatePolicy(Document document) throws CoreException { |
| Node root = document.getDocumentElement(); |
| reset(); |
| |
| if (root.getNodeName().equals(TAG_POLICY)==false) |
| throwCoreException("'"+TAG_POLICY+Messages.UpdatePolicy_policyExpected, null); //$NON-NLS-1$ |
| |
| NodeList nodes = root.getChildNodes(); |
| |
| for (int i=0; i<nodes.getLength(); i++) { |
| Node child = nodes.item(i); |
| if (child.getNodeType() != Node.ELEMENT_NODE) |
| continue; |
| String tag = child.getNodeName(); |
| if (tag.equals(TAG_URL_MAP)) |
| processMapNode(child); |
| } |
| } |
| private void processMapNode(Node node) throws CoreException { |
| String pattern = getAttribute(node, ATT_PATTERN); |
| String urlName = getAttribute(node, ATT_URL); |
| String type = getAttribute(node, ATT_TYPE); |
| |
| assertNotNull(ATT_PATTERN, pattern); |
| assertNotNull(ATT_URL, urlName); |
| |
| // empty url means feature is not updateable |
| if (urlName.trim().length() == 0) { |
| addUpdateEntry(pattern, null, type); |
| return; |
| } |
| |
| try { |
| //PAL foundation |
| String decodedValue = UpdateURLDecoder.decode(urlName, "UTF-8"); //$NON-NLS-1$ |
| URL url = new URL(decodedValue); |
| addUpdateEntry(pattern, url, type); |
| } catch (MalformedURLException e) { |
| throwCoreException(Messages.UpdatePolicy_invalidURL+urlName, null); |
| } catch (UnsupportedEncodingException e) { |
| } |
| } |
| |
| private void assertNotNull(String name, String value) throws CoreException { |
| if (value==null) |
| throwCoreException(name+Messages.UpdatePolicy_nameNoNull, null); |
| } |
| |
| private String getAttribute(Node node, String name) { |
| NamedNodeMap attMap = node.getAttributes(); |
| Node att = attMap.getNamedItem(name); |
| if (att==null) return null; |
| return att.getNodeValue(); |
| } |
| |
| private void addUpdateEntry(String pattern, URL url, String type) { |
| if (pattern.equalsIgnoreCase("*")) {//$NON-NLS-1$ |
| if (type == null) |
| defaultSite = new MapSite(url); |
| else if (type.equals(ATT_TYPE_VALUE_UPDATE)) |
| defaultSite = new MapSite(url); |
| else if (type.equals(ATT_TYPE_VALUE_DISCOVERY)) |
| defaultDiscoverySite = new MapSite(url); |
| else { |
| defaultSite = new MapSite(url); |
| defaultDiscoverySite = new MapSite(url); |
| } |
| } else { |
| if (type == null ) |
| entries.add(new UpdateMapEntry(pattern, url)); |
| else if (type.equals(ATT_TYPE_VALUE_UPDATE)) |
| entries.add(new UpdateMapEntry(pattern, url)); |
| else if (type.equals(ATT_TYPE_VALUE_DISCOVERY)) |
| discoveryEntries.add(new UpdateMapEntry(pattern, url)); |
| else { |
| entries.add(new UpdateMapEntry(pattern, url)); |
| discoveryEntries.add(new UpdateMapEntry(pattern, url)); |
| } |
| } |
| } |
| |
| private void throwCoreException(String message, Throwable e) throws CoreException { |
| String fullMessage = Messages.UpdatePolicy_UpdatePolicy+message; |
| throw Utilities.newCoreException(fullMessage, 0, e); |
| } |
| } |