/**********************************************************************
Copyright (c) 2004 Erik Bengtson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
2005 Andy Jefferson - addition of states
2007 Andy Jefferson - moved extensions to this class, javadocs
...
**********************************************************************/
package org.jpox.metadata;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import org.jpox.ObjectManagerImpl;
import org.jpox.exceptions.JPOXException;
import org.jpox.util.Localiser;
/**
* Base class for all MetaData.
* <h3>MetaData Lifecycle</h3>
* The states represent the lifecycle of a MetaData object. The lifecycle goes as follows :
* <OL>
* <LI>MetaData object is created (values passed in from a parsed file, or manually generated)</LI>
* <LI>MetaData object is populated (maybe pass in a class that it represents, creating any additional
* information that wasn't in the initial data).</LI>
* <LI>MetaData object is initialised (any internal arrays are set up, and additions of data is blocked
* from this point).
* <LI>MetaData object is added to with runtime information like actual column names and types in use.</LI>
* </OL>
* <h3>MetaData Extensability</h3>
* <p>
* All MetaData elements are extensible with extensions for a "vendor-name". Extensions take the form
* of a key and a value.
*
* @version $Revision: 1.12 $
*/
public class MetaData implements Serializable
{
protected static Localiser LOCALISER = Localiser.getInstance("org.jpox.metadata.Localisation",
ObjectManagerImpl.class.getClassLoader());
/** State representing the start state of MetaData, representing the initial values passed in. */
public static final int METADATA_CREATED_STATE = 0;
/** State reflecting that MetaData has been populated with real class definition adding any defaulted info. */
public static final int METADATA_POPULATED_STATE = 1;
/** State reflecting that MetaData object has been initialised with any internal info required. */
public static final int METADATA_INITIALISED_STATE = 2;
/** State reflecting that MetaData object has been modified with usage information (e.g defaulted column names). */
public static final int METADATA_USED_STATE = 3;
/** State of the MetaData. */
protected int metaDataState = METADATA_CREATED_STATE;
/** Parent MetaData object, allowing hierarchical MetaData structure. */
protected MetaData parent;
/** Vendor name used by JPOX for extensions. */
public static final String JPOX_VENDOR_NAME = "jpox";
/** List of extensions for this MetaData element. */
protected Collection extensions = null;
/**
* Constructor. Taking the parent MetaData object (if any).
* @param parent The parent MetaData object.
*/
public MetaData(MetaData parent)
{
this.parent = parent;
}
// -------------------------------- Mutators -------------------------------
/**
* Method to populate the object.
* The state changes to "POPULATED" after this call.
*/
public void populate()
{
setPopulated();
}
/**
* Method to initialise the object.
* The state changes to "INITIALISED" after this call.
*/
public void initialise()
{
setInitialised();
}
/**
* Utility to set the state as initialised.
*/
public void setInitialised()
{
metaDataState = METADATA_INITIALISED_STATE;
}
/**
* Utility to set the state as populated.
*/
public void setPopulated()
{
metaDataState = METADATA_POPULATED_STATE;
}
/**
* Utility to set the state as used.
*/
public void setUsed()
{
metaDataState = METADATA_USED_STATE;
}
/**
* Convenience method to set the parent of this MetaData element.
* Only valid for calling until the MetaData is populated/initialised and throws a JPOXException
* thereafter.
* @param md The parent
*/
public void setParent(MetaData md)
{
if (isPopulated() || isInitialised())
{
throw new JPOXException("Cannot set parent of " + this + " since it is already populated/initialised");
}
this.parent = md;
}
/**
* Append new Extension (for the specified vendor).
* Will throw an InvalidMetaDataException if the input is invalid.
* @param vendor vendor-name tag value
* @param key key tag value
* @param value value tag value
*/
public void addExtension(String vendor, String key, String value)
{
if (vendor == null ||
(vendor.equalsIgnoreCase(JPOX_VENDOR_NAME) && (key == null || value == null)))
{
throw new InvalidMetaDataException(LOCALISER, "044160", vendor, key, value);
}
if (vendor.equals(JPOX_VENDOR_NAME) && hasExtension(key))
{
// Remove any existing value
removeExtension(key);
}
if (extensions == null)
{
// First extensions so allocate the collection. We dont need ordering so use HashSet
extensions = new HashSet(2);
}
extensions.add(new ExtensionMetaData(vendor, key, value));
}
/**
* Append new Extension (for JPOX).
* Will throw an InvalidMetaDataException if the input is invalid.
* @param key key tag value
* @param value value tag value
*/
public void addExtension(String key, String value)
{
addExtension(JPOX_VENDOR_NAME, key, value);
}
/**
* Method to remove a (JPOX) MetaData extension.
* @param key Key of the tag
*/
public void removeExtension(String key)
{
if (extensions == null)
{
return;
}
Iterator iter = extensions.iterator();
while (iter.hasNext())
{
ExtensionMetaData ex = (ExtensionMetaData)iter.next();
if (ex.getVendorName().equalsIgnoreCase(JPOX_VENDOR_NAME) && ex.getKey().equals(key))
{
iter.remove();
break;
}
}
}
// -------------------------------- Accessors ------------------------------
/**
* Accessor for the parent MetaData object.
* @return Parent MetaData object.
*/
public MetaData getParent()
{
return parent;
}
/**
* Accessor for whether the object state is "populated" (at least).
* @return Whether it is populated.
*/
public boolean isPopulated()
{
return (metaDataState >= METADATA_POPULATED_STATE);
}
/**
* Accessor for whether the object state is "initialised" (at least).
* @return Whether it is initialised
*/
public boolean isInitialised()
{
return (metaDataState >= METADATA_INITIALISED_STATE);
}
/**
* Accessor for whether the object state is "used" (has been modified with usage info).
* @return Whether it is used.
*/
public boolean isUsed()
{
return (metaDataState == METADATA_USED_STATE);
}
/**
* Accessor for the number of extensions.
* @return Number of extensions
*/
public int getNoOfExtensions()
{
if (extensions == null)
{
return 0;
}
return extensions.size();
}
/**
* Accessor for the extensions.
* @return The extensions.
**/
public ExtensionMetaData[] getExtensions()
{
if (extensions == null || extensions.size() == 0)
{
return null;
}
return (ExtensionMetaData[])extensions.toArray(new ExtensionMetaData[extensions.size()]);
}
/**
* Accessor for whether an extension exists (for JPOX).
* @param key The key of the extension
* @return Whether the extension exists
*/
public boolean hasExtension(String key)
{
if (extensions == null || key == null)
{
return false;
}
Iterator iter = extensions.iterator();
while (iter.hasNext())
{
ExtensionMetaData ex = (ExtensionMetaData)iter.next();
if (ex.getKey().equals(key) && ex.getVendorName().equalsIgnoreCase(JPOX_VENDOR_NAME))
{
return true;
}
}
return false;
}
/**
* Accessor for the value of a particular extension (for JPOX).
* @param key The key of the extension
* @return The value of the extension (null if not existing)
*/
public String getValueForExtension(String key)
{
if (extensions == null || key == null)
{
return null;
}
Iterator iter=extensions.iterator();
while (iter.hasNext())
{
ExtensionMetaData ex = (ExtensionMetaData)iter.next();
if (ex.getKey().equals(key) && ex.getVendorName().equalsIgnoreCase(JPOX_VENDOR_NAME))
{
return ex.getValue();
}
}
return null;
}
/**
* Accessor for the value of a particular extension (for JPOX), but
* splitting it into separate parts. This is for extension tags that have a
* value as comma separated.
* @param key The key of the extension
* @return The value(s) of the extension (null if not existing)
*/
public String[] getValuesForExtension(String key)
{
if (extensions == null || key == null)
{
return null;
}
Iterator iter = extensions.iterator();
while (iter.hasNext())
{
ExtensionMetaData ex = (ExtensionMetaData)iter.next();
if (ex.getKey().equals(key) && ex.getVendorName().equalsIgnoreCase(JPOX_VENDOR_NAME))
{
return MetaDataUtils.getInstance().getValuesForCommaSeparatedAttribute(ex.getValue());
}
}
return null;
}
// -------------------------------- Utilities ------------------------------
/**
* Accessor for a string representation of the object.
* @return a string representation of the object.
*/
public String toString()
{
return toString("","");
}
/**
* Returns a string representation of the object.
* @param prefix prefix string
* @param indent indent string
* @return a string representation of the object.
*/
public String toString(String prefix, String indent)
{
if (extensions == null || extensions.size() == 0)
{
return "";
}
StringBuffer sb = new StringBuffer();
Iterator iter = extensions.iterator();
while (iter.hasNext())
{
ExtensionMetaData ex = (ExtensionMetaData)iter.next();
sb.append(prefix).append(ex.toString()).append("\n");
}
return sb.toString();
}
}