blob: 9b55b0225626f20d1fb43fa26986ff79a40b4f96 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 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 API will be deleted in a future release. See bug 311590 for details.
*/
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;
}
}