alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.api.registry;

import org.netbeans.modules.registry.ApiContextFactory;
import org.netbeans.modules.registry.OrderingSupport;
import org.netbeans.spi.registry.BasicContext;
import org.netbeans.spi.registry.MergedContextProvider;
import org.netbeans.spi.registry.ResettableContext;
import org.netbeans.spi.registry.SpiUtils;
import org.openide.ErrorManager;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;

import java.awt.*;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.List;

/**
 * This API class is representing a context which consists of a set of 
 * name-to-object bindings. It contains methods for examining and 
 * updating these bindings. The contexts are composed into
 * hierarchical collection which form configuration system. The 
 * configuration system allows applicatioRootns to store and retrieve
 * data. How data are stored is not specified by the Registry API.
 * It is up to provided implementation of Registry SPI to properly
 * document it and that documentation must be consulted.
 *
 * 

The hierarchical collection of contexts is similar to folders * in a hierarchical file system. There exist a root context containing * all the contexts. The name of this context is "/" * and it is also absolute name of the context. The names * of individual contexts cannot be empty, but does not have to * be unique. However, absolute name of the context must be * unique. Children of the root context have absolute names of * "/" + context name. All other contexts have absolute names of * parent's context absolute name + "/" + context name. * *

The context can contain the subcontext and binding with the same name * and they will coexist. * *

The context has getters and setters for all primitive data types, * for a few basic object types (eg. Color, URL, etc.) and for Object. * As was said above the storage format is specific per Registry SPI * implementation, but following is valid for all backends: *

    *
  • primitive data types: the primitive data type is converted into corresponding * object counterpart and the object is stored. Retrieval of the primitive * value does the opposite: the object is retrieved and converted to primitive data * type. Each context implementation must support storage of these data types.
  • *
  • supported basic object types: these object types in addition to * basic data types must be persistable by all context implementations.
  • *
  • any other Object: it is up to specific Registry SPI implementation * to document what is supported and how in this area.
  • *
* *

Getters do not throw exceptions. They accept default value parameter which * is returned if value does not exist or it could not be read. The exceptions are logged. * *

Putting null value removes the binding. * *

Rename of binding is not supported because no useful usecase was found. * *

All methods are properly synchronized and can be accessed from multiple * threads without any synchronization on the client side. During the modification is * whole hierarchy of contexts exclusively locked. * *

TBD: Names restrictions: length, valid characters, etc. * The context name nor binding name cannot contain "/" character. * * @author David Konecny */ public final class Context { /** SPI context to which this API context delegates most of the calls. */ BasicContext delegate; /** Cache of the created API contexts. The key is is SPI context and * value is WeakReference to API context. */ private static WeakHashMap contextCache = new WeakHashMap(); // This should theoretically be in ApiContextFactoryImpl, but that would // mean that variable ApiContextFactory.DEFAULT would not initialized // before ApiContextFactoryImpl was loaded into VM what can be sometimes // too late. On the other hand Context class is loaded as one of the first classes. // That's why it is here. private static final Mutex.Privileged privilegedMutex = new Mutex.Privileged(); private static final Mutex mutex = new Mutex (privilegedMutex); static { ApiContextFactory.DEFAULT = new ApiContextFactoryImpl(); } private static BasicContext defaultRootContext; private Context(BasicContext delegate) { this.delegate = delegate; } /////////////////////////// // Context related methods: /////////////////////////// /** * Getter for default instance of registry. * *

Application specific documentation must be consulted to learn which * exact implementation will be returned by this method. For example for * NetBeans it can be implementation working on top of the root of * SystemFileSystem. * * @return default root context */ public static Context getDefault() { return Context.getApiContext(getRC()); } /** * Returns context, that merges all its delegates. See JavaDoc overview for more details. * @param delegates array of contexts that should be merged * @return merged context * @since 1.6 */ public static Context merge (final Context[] delegates) { BasicContext mergedBasic = SpiUtils.merge(new MergedContextProvider() { public void addPropertyChangeListener(PropertyChangeListener listener) { } public void removePropertyChangeListener(PropertyChangeListener listener) { } public BasicContext[] getDelegates() { List basicDelegates = new ArrayList(); for (int i = 0; i < delegates.length; i++) { Context delegate = delegates[i]; basicDelegates.add(delegate.delegate); } return (BasicContext[])basicDelegates.toArray(new BasicContext[0]); } }); return SpiUtils.createContext(mergedBasic); } /** * Name of the context. * * @return name of the context; cannot be null */ public String getContextName() { return delegate.getContextName(); } /** * Gets root context. * * @return root context */ public Context getRootContext() { return getApiContext(delegate.getRootContext()); } /** * Getter for the absolute name of the context, eg. "/mymodule/mysettings". * It is full path from the root context. * * @return absolute name of this context; cannot be null */ public String getAbsoluteContextName() { BasicContext ctx = delegate; StringBuffer sb = new StringBuffer(ctx.getContextName()); while (ctx.getParentContext() != null) { ctx = ctx.getParentContext(); if (ctx.getContextName().equals("/")) { sb.insert(0, ctx.getContextName()); } else { sb.insert(0, "/"); sb.insert(0, ctx.getContextName()); } } return sb.toString(); } /** * Retrieve subcontext of the given name. The multi-level path * is accepted as subcontext name (eg. "sub1/sub2/mysub"). * * @param subcontextName multi-level subcontext name to retrieve * @return Context or null if subcontext does not exist */ public Context getSubcontext(String subcontextName) { Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterReadAccess(); StringTokenizer tok = new StringTokenizer(subcontextName, "/"); // NOI18N BasicContext ctx = delegate; while (tok.hasMoreTokens() && ctx != null) { String name = tok.nextToken(); ctx = ctx.getSubcontext(name); } return getApiContext(ctx); } finally { mp.exitReadAccess(); } } /** * Retrieve parent context. * * @return parent context or null in case of root context */ public Context getParentContext() { Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterReadAccess(); return getApiContext(delegate.getParentContext()); } finally { mp.exitReadAccess(); } } /** * Create subcontext of the given name. The multi-level path * is accepted as subcontext name (eg. "sub1/sub2/mysub"). All * intermediate subcontexts which do not exist will be created. If * subcontext already exist it is just retrieved. * * @param subcontextName multi-level subcontext name to create * @return created or retrieved context * @throws ContextException thrown when subcontext cannot be created */ public Context createSubcontext(String subcontextName) throws ContextException { Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterWriteAccess(); StringTokenizer tok = new StringTokenizer(subcontextName, "/"); // NOI18N BasicContext ctx = delegate; while (tok.hasMoreTokens()) { String name = tok.nextToken(); BasicContext ctx2 = ctx.getSubcontext(name); if (ctx2 != null) { ctx = ctx2; } else { ctx = ctx.createSubcontext(name); } } return getApiContext(ctx); } finally { mp.exitWriteAccess(); } } /** * Destroy subcontext of the given name. Destroying context deletes * also all its data recursively, ie. all bindings, attributes * and its subcontexts. The multi-level path is accepted * (eg. "sub1/sub2/mysub"). * * @param subcontextName multi-level name of existing subcontext * @throws ContextException thrown when subcontext cannot be deleted */ public void destroySubcontext(String subcontextName) throws ContextException { int index = subcontextName.lastIndexOf('/'); BasicContext ctx = delegate; if (index != -1) { ctx = getSubcontext(subcontextName.substring(0, index)).delegate; subcontextName = subcontextName.substring(index+1); } Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterWriteAccess(); ctx.destroySubcontext(subcontextName); } finally { mp.exitWriteAccess(); } } /////////////////////////////// // Context enumeration methods: /////////////////////////////// /** * Retrieve names of all subcontexts of this context. * * @return collection of Strings, ie. names of all subcontexts in the context; * cannot be null */ public Collection/**/ getSubcontextNames() { Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterReadAccess(); return delegate.getSubcontextNames(); } finally { mp.exitReadAccess(); } } /** * Retrieve names of all bindings in this context. * * @return collection of Strings, ie. names of all bindings in the context; * cannot be null */ public Collection/**/ getBindingNames() { Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterReadAccess(); return delegate.getBindingNames(); } finally { mp.exitReadAccess(); } } /** * Retrieve names of all attributes in this context. * * @return collection of Strings, ie. names of all attribute in the context; * cannot be null */ public Collection/**/ getAttributeNames(String bindingName) { Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterReadAccess(); return delegate.getAttributeNames(bindingName); } finally { mp.exitReadAccess(); } } ///////////////////////////////////// // Ordered context enumerations methods: ///////////////////////////////////// /** Store full order of the context content. The passed * list can contain only String instances which are names * of a context's binding or context's subcontext. The context's * subcontext name must be appended with "/" character to distinguish subcontext * name from binding name. For any other value the * IllegalArgumentException will be thrown. * * @param names full order of the objects in the context; see above for * expected values in the list */ public void orderContext(List names) { StringBuffer value = new StringBuffer(); Iterator it = names.iterator(); while (it.hasNext()) { Object o = it.next(); if (!(o instanceof String)) { throw new IllegalArgumentException("OrderContext: Passed list contains item which is not String - "+o); } String item = (String)o; // this checking might be ommited. the fullorder can contain invalid items // and they will be ignored. therefore I'm not doing this under write lock as // it should be and if there are perf or other problems the checkItem // method can be removed completely. checkItem(item); value.append(item); value.append(","); } if (value.length() > 0) { value.setLength(value.length()-1); } setAttribute(null, "fullorder", value.toString()); } private void checkItem(String name) { if (name.endsWith("/")) { name = name.substring(0, name.length()-1); if (getSubcontext(name) == null) { throw new IllegalArgumentException("OrderContext: Passed list contains non-existing subcontext - "+name); } } else { if (!getBindingNames().contains(name)) { throw new IllegalArgumentException("OrderContext: Passed list contains non-existing binding - "+name); } } } /** * Method for listing items of the context ordered. The returned list * will contain instances of all direct subcontexts and all bound * objects in this context. * *

See Javadoc overview for how the context items are sorted. * If context content was ordered by {@link #orderContext} method * then the items will be listed in this order (order of items which were * not specified in the full order is undefined). * * @return collection of ordered Objects, ie. both instances of bound * Objects and Contexts representing subcontexts are returned. */ public List getOrderedObjects() { Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterReadAccess(); List ar = new ArrayList(); Iterator it = OrderingSupport.DEFAULT.getOrderedNames(this).iterator(); while (it.hasNext()) { String item = (String)it.next(); if (item.endsWith("/")) { Context ctx = getSubcontext(item.substring(0, item.length()-1)); if (ctx != null) { ar.add(ctx); } } else { Object o = getObject(item, null); if (o != null) { ar.add(o); } } } return ar; } finally { mp.exitReadAccess(); } } /** * Method listing ordered names of the context items. The returned list * will contain names of all direct subcontexts and all bound * objects in this context. The subcontext names are appended * with "/" character. It can be used to distinguish subcontext name * and binding name when they are the same. * *

See the JavaDoc for {@link #getOrderedObjects} method for * more details about ordering itself. * * @return collection of Strings; contains binding names and subcontext * names appended with "/" character * * @since 1.4 */ public List getOrderedNames() { Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterReadAccess(); return OrderingSupport.DEFAULT.getOrderedNames(this); } finally { mp.exitReadAccess(); } } /////////////////////////////// // Bindings related methods: /////////////////////////////// /** * Retrieve named object from the context. * *

If retrieved object is instance of {@link ObjectRef} it is recursively * dereferenced and the value of ObjectRef instance if returned instead. * If dereferenced value is null or ObjectRef is invalid then the ObjectRef * instance is returned. See {@link #getRef} if you need to retrieve * ObjectRef directly. * * @param bindingName the name of the object to retrieve; cannot be empty * @param defaultValue default value returned in case the binding does not * exist. * @return retrieved value or defaultValue if this binding does not exist */ public Object getObject(String bindingName, Object defaultValue) { Object o = getObject(bindingName); if (o instanceof ObjectRef) { // it is ObjectRef -> dereference it: Object origin = o; while (o != null && o instanceof ObjectRef) { o = ((ObjectRef)o).getObject(); } // if the resulted value is null then return ObjectRef: if (o == null) { o = origin; } } if (o == null) { return defaultValue; } else { return o; } } /** * Retrieve directly the bounded ObjectRef instance. The {@link #getObject} * does recursive traversal of ObjectRef and returns directly the referenced value. * * @param bindingName the name of the object to retrieve; cannot be empty * @return instance of the ObjectRef or null if bound object is not ObjectRef */ public ObjectRef getRef(String bindingName) { Object o = getObject(bindingName); if (o instanceof ObjectRef) { return (ObjectRef)o; } else { return null; } } private Object getObject(String bindingName) { Object o = null; Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterReadAccess(); o = delegate.lookupObject(bindingName); } catch (ContextException ex) { ErrorManager.getDefault().annotate(ex, NbBundle.getMessage(Context.class, "MSG_get_object", bindingName, getAbsoluteContextName())); ErrorManager.getDefault().notify(ErrorManager.ERROR, ex); } finally { mp.exitReadAccess(); } return o; } /** * Binds a name to an object and store the object in context. Use null * value to remove binding. * *

See class overview JavaDoc for more details about how * the objects are stored and which object types are supported. * * @param bindingName the name to bind; cannot be empty * @param value the object to bind; null is allowed and means * deletion of the binding */ public void putObject(String bindingName, Object value) { Mutex.Privileged mp = getMutexPrivileged(); try { mp.enterWriteAccess(); delegate.bindObject(bindingName, value); } catch (ContextException ex) { ErrorManager.getDefault().annotate(ex, NbBundle.getMessage(Context.class, "MSG_put_object", bindingName, getAbsoluteContextName())); ErrorManager.getDefault().notify(ErrorManager.ERROR, ex); } finally { mp.exitWriteAccess(); } } /** * Retrieve String value. * * @param bindingName binding name * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public String getString(String bindingName, String defaultValue) { Object o = getObject(bindingName, null); if (o == null || !(o instanceof String) ) { return defaultValue; } else { return (String)o; } } /** * Binds a name to the String and store it in the context. Use null * value to remove binding. * * @param bindingName binding name * @param value value */ public void putString(String bindingName, String value) { putObject(bindingName, value); } /** * Retrieve integer value. * * @param bindingName binding name * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public int getInt(String bindingName, int defaultValue) { Object o = getObject(bindingName, null); if (o == null) { return defaultValue; } else if (o instanceof Integer) { return ((Integer)o).intValue(); } else if (o instanceof String) { return Integer.parseInt((String)o); } else { return defaultValue; } } /** * This method converts the passed integer to Integer * object and binds it into context. * Use {@link #putObject} with null value to remove binding. * * @param bindingName binding name * @param value value */ public void putInt(String bindingName, int value) { putObject(bindingName, new Integer(value)); } /** * Retrieve long value. * * @param bindingName binding name * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public long getLong(String bindingName, long defaultValue) { Object o = getObject(bindingName, null); if (o == null) { return defaultValue; } else if (o instanceof Long) { return ((Long)o).longValue(); } else if (o instanceof String) { return Long.parseLong((String)o); } else { return defaultValue; } } /** * This method converts the passed long to Long * object and binds it into context. * Use {@link #putObject} with null value to remove binding. * * @param bindingName binding name * @param value value */ public void putLong(String bindingName, long value) { putObject(bindingName, new Long(value)); } /** * Retrieve boolean value. * * @param bindingName binding name * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public boolean getBoolean(String bindingName, boolean defaultValue) { Object o = getObject(bindingName, null); if (o == null) { return defaultValue; } else if (o instanceof Boolean) { return ((Boolean)o).booleanValue(); } else if (o instanceof String) { return Boolean.valueOf((String)o).booleanValue(); } else { return defaultValue; } } /** * This method converts the passed boolean to Boolean * object and binds it into context. * Use {@link #putObject} with null value to remove binding. * * @param bindingName binding name * @param value value */ public void putBoolean(String bindingName, boolean value) { putObject(bindingName, Boolean.valueOf(value)); } /** * Retrieve float value. * * @param bindingName binding name * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public float getFloat(String bindingName, float defaultValue) { Object o = getObject(bindingName, null); if (o == null) { return defaultValue; } else if (o instanceof Float) { return ((Float)o).floatValue(); } else if (o instanceof String) { return Float.parseFloat((String)o); } else { return defaultValue; } } /** * This method converts the passed float to Float * object and binds it into context. * Use {@link #putObject} with null value to remove binding. * * @param bindingName binding name * @param value value */ public void putFloat(String bindingName, float value) { putObject(bindingName, new Float(value)); } /** * Retrieve double value. * * @param bindingName binding name * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public double getDouble(String bindingName, double defaultValue) { Object o = getObject(bindingName, null); if (o == null) { return defaultValue; } else if (o instanceof Double) { return ((Double)o).doubleValue(); } else if (o instanceof String) { return Double.parseDouble((String)o); } else { return defaultValue; } } /** * This method converts the passed double to Double * object and binds it into context. * Use {@link #putObject} with null value to remove binding. * * @param bindingName binding name * @param value value */ public void putDouble(String bindingName, double value) { putObject(bindingName, new Double(value)); } /** * Retrieve font value. * * @param bindingName binding name * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public Font getFont(String bindingName, Font defaultValue) { Object o = getObject(bindingName, null); if (o == null) { return defaultValue; } else if (o instanceof Font) { return (Font)o; } else if (o instanceof String) { return Font.decode((String)o); } else { return defaultValue; } } /** * This is just convenient method. Its functionality is equal * to {@link #putObject}. Use null value to remove binding. * * @param bindingName binding name * @param value value */ public void putFont(String bindingName, Font value) { putObject(bindingName, value); } /** * Retrieve color value. * * @param bindingName binding name * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public Color getColor(String bindingName, Color defaultValue) { Object o = getObject(bindingName, null); if (o == null) { return defaultValue; } else if (o instanceof Color) { return (Color)o; } else if (o instanceof String) { return Color.decode((String)o); } else { return defaultValue; } } /** * This is just convenient method. Its functionality is equal * to {@link #putObject}. Use null value to remove binding. * * @param bindingName binding name * @param value value */ public void putColor(String bindingName, Color value) { putObject(bindingName, value); } /** * Retrieve URL value. * * @param bindingName binding name * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public URL getURL(String bindingName, URL defaultValue) { Object o = getObject(bindingName, null); if (o == null) { return defaultValue; } else if (o instanceof URL) { return (URL)o; } else if (o instanceof String) { try { return new URL((String)o); } catch (MalformedURLException ex) { return defaultValue; } } else { return defaultValue; } } /** * This is just convenient method. Its functionality is equal * to {@link #putObject}. Use null value to remove binding. * * @param bindingName binding name * @param value value */ public void putURL(String bindingName, URL value) { putObject(bindingName, value); } /** * Retrieve string array by reading stored string and splitting it by the * given separator into array of Strings. * * @param bindingName binding name * @param separator separtor character * @param defaultValue default value returned if this binding does not exist * @return retrieved value or defaultValue if this binding does not exist */ public String[] getStringArray(String bindingName, char separator, String[] defaultValue) { String value = getString(bindingName, null); if (value == null) { return defaultValue; } StringTokenizer tok = new StringTokenizer(value, Character.toString(separator)); String sa[] = new String[tok.countTokens()]; int index = 0; while (tok.hasMoreTokens()) { sa[index] = tok.nextToken(); index++; } return sa; } /** * Store array of strings. The strings are compound into one String and * separated by separator. The rest is same as in putString() method. * Use null value to remove binding. * * @param bindingName binding name * @param value value * @param separator separator character */ public void putStringArray(String bindingName, char separator, String[] value) { if (value == null) { putString(bindingName, null); return; } StringBuffer sb = new StringBuffer(); for (int i=0; i

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.