| /******************************************************************************* |
| * Copyright (c) 2000, 2009 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.core.model; |
| |
| import java.lang.reflect.Array; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.MissingResourceException; |
| import java.util.ResourceBundle; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.PlatformObject; |
| import org.eclipse.update.core.Feature; |
| import org.eclipse.update.core.SiteManager; |
| import org.eclipse.update.internal.core.Messages; |
| import org.eclipse.update.internal.core.UpdateCore; |
| import org.eclipse.update.internal.core.UpdateManagerUtils; |
| |
| /** |
| * Root model object. Extended by all model objects. |
| * <p> |
| * This class cannot be instantiated and must be subclassed. |
| * </p> |
| * <p> |
| * <b>Note:</b> This class/interface is part of an interim API that is still under development and expected to |
| * change significantly before reaching stability. It is being made available at this early stage to solicit feedback |
| * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken |
| * (repeatedly) as the API evolves. |
| * </p> |
| * @deprecated The org.eclipse.update component has been replaced by Equinox p2. This |
| * provisional API was never promoted to stable API, and may be removed from a future release of the platform. |
| */ |
| public abstract class ModelObject extends PlatformObject { |
| |
| private boolean readOnly = false; |
| |
| private static final String KEY_PREFIX = "%"; //$NON-NLS-1$ |
| private static final String KEY_DOUBLE_PREFIX = KEY_PREFIX + KEY_PREFIX; |
| |
| private static Map bundles; |
| |
| /** |
| * Creates a base model object. |
| * |
| * @since 2.0 |
| */ |
| protected ModelObject() { |
| } |
| |
| /** |
| * Checks that this model object is writeable. A runtime exception |
| * is thrown if it is not. |
| * |
| * @since 2.0 |
| */ |
| protected final void assertIsWriteable() { |
| Assert.isTrue(!isReadOnly(), Messages.ModelObject_ModelReadOnly); |
| } |
| |
| /** |
| * Sets this model object and all of its descendents to be read-only. |
| * Subclasses may extend this implementation. |
| * |
| * @see #isReadOnly |
| * @since 2.0 |
| */ |
| public void markReadOnly() { |
| readOnly = true; |
| } |
| |
| /** |
| * Returns whether or not this model object is read-only. |
| * |
| * @return <code>true</code> if this model object is read-only, |
| * <code>false</code> otherwise |
| * @see #markReadOnly |
| * @since 2.0 |
| */ |
| public boolean isReadOnly() { |
| return readOnly; |
| } |
| |
| /** |
| * Delegate setting of read-only |
| * |
| * @param o object to delegate to. Must be of type ModelObject. |
| * @see #isReadOnly |
| * @since 2.0 |
| */ |
| protected void markReferenceReadOnly(ModelObject o) { |
| if (o == null) |
| return; |
| o.markReadOnly(); |
| } |
| |
| /** |
| * Delegate setting of read-only |
| * |
| * @param o object array to delegate to. Each element must be of type ModelObject. |
| * @see #isReadOnly |
| * @since 2.0 |
| */ |
| protected void markListReferenceReadOnly(ModelObject[] o) { |
| if (o == null) |
| return; |
| for (int i = 0; i < o.length; i++) { |
| o[i].markReadOnly(); |
| } |
| } |
| |
| /** |
| * Resolve the model element. This method allows any relative URL strings |
| * to be resolved to actual URL. It also allows any translatable strings |
| * to be localized. |
| * |
| * Subclasses need to override this method to perform the actual resolution. |
| * @param base base URL. |
| * @param bundleURL resource bundle URL. |
| * @exception MalformedURLException |
| * @since 2.0 |
| */ |
| public void resolve(URL base, URL bundleURL) throws MalformedURLException { |
| return; |
| } |
| |
| /** |
| * Delegate resolution to referenced model |
| * |
| * @param o object to delegate to. Must be of type ModelObject. |
| * @param url base URL. |
| * @param bundleURL resource bundle URL. |
| * @exception MalformedURLException |
| * @since 2.0 |
| */ |
| protected void resolveReference(ModelObject o, URL url, URL bundleURL) throws MalformedURLException { |
| if (o == null) |
| return; |
| o.resolve(url, bundleURL); |
| } |
| |
| /** |
| * Delegate resolution to list of referenced models |
| * |
| * @param o object array to delegate to. Each element must be of type ModelObject. |
| * @param url base URL. |
| * @param bundleURL resource bundle URL. |
| * @exception MalformedURLException |
| * @since 2.0 |
| */ |
| protected void resolveListReference(ModelObject[] o, URL url, URL bundleURL) throws MalformedURLException { |
| if (o == null) |
| return; |
| for (int i = 0; i < o.length; i++) { |
| o[i].resolve(url, bundleURL); |
| } |
| } |
| |
| /** |
| * Resolve a URL based on context |
| * |
| * @param context base URL. |
| * @param bundleURL resource bundle URL. |
| * @param urlString url string from model. |
| * @return URL, or <code>null</code>. |
| * @exception MalformedURLException |
| * @since 2.0 |
| */ |
| protected URL resolveURL(URL context, URL bundleURL, String urlString) throws MalformedURLException { |
| |
| // URL string was not specified |
| if (urlString == null || urlString.trim().equals("")) //$NON-NLS-1$ |
| return null; |
| |
| // check to see if we have NL-sensitive URL |
| String resolvedUrlString = resolveNLString(bundleURL, urlString); |
| |
| resolvedUrlString = resolvePlatfromConfiguration(resolvedUrlString); |
| |
| // if we don't have a base url, use only the supplied string |
| if (context == null) |
| return new URL(resolvedUrlString); |
| |
| // otherwise return new URL in context of base URL |
| return new URL(context, resolvedUrlString); |
| } |
| /** |
| * Resolves the URL based on platfrom Configuration |
| * $os$\$ws$\license.txt will become |
| * win32\win32\license.txt on a system where os=win32 and ws=win32 |
| * |
| * @param resolvedUrlString |
| * @return String |
| */ |
| private String resolvePlatfromConfiguration(String resolvedUrlString) { |
| int osIndex = resolvedUrlString.indexOf("$os$"); //$NON-NLS-1$ |
| if (osIndex != -1) |
| return getExtendedString(resolvedUrlString); |
| |
| int wsIndex = resolvedUrlString.indexOf("$ws$"); //$NON-NLS-1$ |
| if (wsIndex != -1) |
| return getExtendedString(resolvedUrlString); |
| |
| int nlIndex = resolvedUrlString.indexOf("$nl$"); //$NON-NLS-1$ |
| if (nlIndex != -1) |
| return getExtendedString(resolvedUrlString); |
| |
| int archIndex = resolvedUrlString.indexOf("$arch$"); //$NON-NLS-1$ |
| if (archIndex != -1) |
| return getExtendedString(resolvedUrlString); |
| |
| return resolvedUrlString; |
| } |
| |
| private String getExtendedString(String resolvedUrlString) { |
| IPath path = new Path(resolvedUrlString); |
| path = getExpandedPath(path); |
| if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_WARNINGS) { |
| UpdateCore.warn("Resolved :" + resolvedUrlString + " as:" + path.toOSString()); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| return path.toOSString(); |
| } |
| |
| private IPath getExpandedPath(IPath path) { |
| String first = path.segment(0); |
| if (first != null) { |
| IPath rest = getExpandedPath(path.removeFirstSegments(1)); |
| if (first.equals("$ws$")) { //$NON-NLS-1$ |
| path = new Path(SiteManager.getWS()).append(rest); |
| } else if (first.equals("$os$")) { //$NON-NLS-1$ |
| path = new Path(SiteManager.getOS()).append(rest); |
| } else if (first.equals("$nl$")) { //$NON-NLS-1$ |
| path = new Path(SiteManager.getNL()).append(rest); |
| } else if (first.equals("$arch$")) { //$NON-NLS-1$ |
| path = new Path(SiteManager.getOSArch()).append(rest); |
| } |
| } |
| return path; |
| } |
| |
| /** |
| * Returns a resource string corresponding to the given argument |
| * value and bundle. |
| * If the argument value specifies a resource key, the string |
| * is looked up in the given resource bundle. If the argument does not |
| * specify a valid key, the argument itself is returned as the |
| * resource string. The key lookup is performed against the |
| * specified resource bundle. If a resource string |
| * corresponding to the key is not found in the resource bundle |
| * the key value, or any default text following the key in the |
| * argument value is returned as the resource string. |
| * A key is identified as a string begining with the "%" character. |
| * Note that the "%" character is stripped off prior to lookup |
| * in the resource bundle. |
| * <p> |
| * For example, assume resource bundle plugin.properties contains |
| * name = Project Name |
| * <pre> |
| * resolveNLString(b,"Hello World") returns "Hello World"</li> |
| * resolveNLString(b,"%name") returns "Project Name"</li> |
| * resolveNLString(b,"%name Hello World") returns "Project Name"</li> |
| * resolveNLString(b,"%abcd Hello World") returns "Hello World"</li> |
| * resolveNLString(b,"%abcd") returns "%abcd"</li> |
| * resolveNLString(b,"%%name") returns "%name"</li> |
| * </pre> |
| * </p> |
| * |
| * @param bundleURL resource bundle url. |
| * @param string translatable string from model |
| * @return string, or <code>null</code> |
| * @since 2.0 |
| */ |
| protected String resolveNLString(URL bundleURL, String string) { |
| |
| if (string == null) |
| return null; |
| |
| String s = string.trim(); |
| |
| if (s.equals("")) //$NON-NLS-1$ |
| return string; |
| |
| if (!s.startsWith(KEY_PREFIX)) |
| return string; |
| |
| if (s.startsWith(KEY_DOUBLE_PREFIX)) |
| return s.substring(1); |
| |
| int ix = s.indexOf(" "); //$NON-NLS-1$ |
| String key = ix == -1 ? s : s.substring(0, ix); |
| String dflt = ix == -1 ? s : s.substring(ix + 1); |
| |
| ResourceBundle b = getResourceBundle(bundleURL); |
| |
| if (b == null) |
| return dflt; |
| |
| try { |
| return b.getString(key.substring(1)); |
| } catch (MissingResourceException e) { |
| return dflt; |
| } |
| } |
| |
| /** |
| * Returns a concrete array type for the elements of the specified |
| * list. The method assumes all the elements of the list are the same |
| * concrete type as the first element in the list. |
| * |
| * @param l list |
| * @return concrete array type, or <code>null</code> if the array type |
| * could not be determined (the list is <code>null</code> or empty) |
| * @since 2.0 |
| */ |
| protected Object[] arrayTypeFor(List l) { |
| if (l == null || l.size() == 0) |
| return null; |
| return (Object[]) Array.newInstance(l.get(0).getClass(), 0); |
| } |
| |
| /** |
| * Returns a concrete array type for the elements of the specified |
| * set. The method assumes all the elements of the set are the same |
| * concrete type as the first element in the set. |
| * |
| * @param s set |
| * @return concrete array type, or <code>null</code> if the array type |
| * could not be determined (the set is <code>null</code> or empty) |
| * @since 2.0 |
| */ |
| protected Object[] arrayTypeFor(Set s) { |
| if (s == null || s.size() == 0) |
| return null; |
| Iterator i = s.iterator(); |
| return (Object[]) Array.newInstance(i.next().getClass(), 0); |
| } |
| |
| /** |
| * Helper method to access resouce bundle for feature. The default |
| * implementation attempts to load the appropriately localized |
| * feature.properties file. |
| * |
| * @param url base URL used to load the resource bundle. |
| * @return resource bundle, or <code>null</code>. |
| * @since 2.0 |
| */ |
| protected ResourceBundle getResourceBundle(URL url) { |
| |
| if (url == null) |
| return null; |
| |
| if (bundles == null) { |
| bundles = new HashMap(); |
| } else { |
| ResourceBundle bundle = (ResourceBundle) bundles.get(url.toExternalForm()); |
| if (bundle != null) |
| return bundle; |
| } |
| |
| ResourceBundle bundle = null; |
| try { |
| url = UpdateManagerUtils.asDirectoryURL(url); |
| ClassLoader l = new URLClassLoader(new URL[] { url }, null); |
| bundle = ResourceBundle.getBundle(getPropertyName(), Locale.getDefault(), l); |
| bundles.put(url.toExternalForm(), bundle); |
| } catch (MissingResourceException e) { |
| UpdateCore.warn(e.getLocalizedMessage() + ":" + url.toExternalForm()); //$NON-NLS-1$ |
| } catch (MalformedURLException e) { |
| UpdateCore.warn(e.getLocalizedMessage()); |
| } |
| return bundle; |
| } |
| |
| /** |
| * Method getPropertyName. |
| * @return String |
| */ |
| protected String getPropertyName() { |
| return Feature.FEATURE_FILE; |
| } |
| |
| } |