/******************************************************************************* | |
* Copyright (c) 2000, 2003 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Common Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v10.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.update.core.model; | |
import java.lang.reflect.*; | |
import java.net.*; | |
import java.util.*; | |
import org.eclipse.core.runtime.*; | |
import org.eclipse.update.core.*; | |
import org.eclipse.update.internal.core.*; | |
/** | |
* 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> | |
*/ | |
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(), Policy.bind("ModelObject.ModelReadOnly")); //$NON-NLS-1$ | |
} | |
/** | |
* 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()); //$NON-NLS-1$ | |
} | |
return bundle; | |
} | |
/** | |
* Method getPropertyName. | |
* @return String | |
*/ | |
protected String getPropertyName() { | |
return Feature.FEATURE_FILE; | |
} | |
} |