/*
* Copyright (C) 2004 The Concord Consortium, Inc.,
* 10 Concord Crossing, Concord, MA 01742
*
* Web Site: http://www.concord.org
* Email: info@concord.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* END LICENSE */
/*
* Last modification information:
* $Revision: 1.17 $
* $Date: 2007-07-25 20:25:34 $
* $Author: scytacki $
*
* Licence Information
* Copyright 2004 The Concord Consortium
*/
package org.concord.otrunk.xml;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
import org.concord.framework.otrunk.OTID;
import org.concord.otrunk.datamodel.OTDataCollection;
import org.concord.otrunk.datamodel.OTDataList;
import org.concord.otrunk.datamodel.OTDataMap;
import org.concord.otrunk.datamodel.OTDataObject;
import org.concord.otrunk.datamodel.OTDataObjectType;
import org.concord.otrunk.datamodel.OTDatabase;
import org.concord.otrunk.datamodel.OTIDFactory;
import org.concord.otrunk.datamodel.OTObjectRevision;
/**
* XMLDataObject
* Class name and description
*
* Date created: Oct 4, 2004
*
* @author scott<p>
*
*/
public class XMLDataObject
implements OTDataObject
{
private static final Logger logger = Logger.getLogger(XMLDataObject.class.getName());
private OTDataObjectType type;
private OTID globalId;
private XMLDatabase database = null;
private OTXMLElement element;
private String localId = null;
private boolean preserveUUID = false;
/**
* If this is true then nulls which are set on this object will
* be saved in a "unset" property on the object. The value is a
* space separated list of properties which are null.
*
* If this is false then nulls simply not recorded. This approach
* is used when this object is layered on top of another object
* and needs to override a value set on that lower object.
*/
private boolean saveNulls = false;
HashMap<String, Object> resources = new LinkedHashMap<String, Object>();
HashMap<String, XMLReferenceInfo> referenceInfoMap = new HashMap<String, XMLReferenceInfo>();
XMLDataObject container = null;
String containerResourceKey = null;
XMLDataObject(OTXMLElement element, OTID id, XMLDatabase db)
{
this.element = element;
globalId = id;
database = db;
}
public void setType(OTDataObjectType type)
{
this.type = type;
}
public OTXMLElement getElement()
{
return element;
}
/* (non-Javadoc)
* @see org.concord.otrunk.OTDataObject#getGlobalId()
*/
public OTID getGlobalId()
{
return globalId;
}
public OTDatabase getDatabase()
{
return database;
}
public void setGlobalId(String id)
{
setGlobalId(OTIDFactory.createOTID(id));
}
public void setGlobalId(OTID id)
{
globalId = id;
}
public boolean setResource(String key, Object resource)
{
return setResource(key, resource, true);
}
/* (non-Javadoc)
* @see org.concord.otrunk.OTDataObject#setResource(java.lang.String, java.lang.Object)
*/
boolean setResource(String key, Object resource, boolean markDirty)
{
Object oldObject;
if(resource == null && !saveNulls) {
oldObject = resources.remove(key);
if (oldObject instanceof OTDataObject) {
database.removeReference(this, (OTDataObject) oldObject);
} else if (oldObject instanceof OTID) {
database.removeReference(this.globalId, (OTID) oldObject);
}
} else {
oldObject = resources.put(key, resource);
if (oldObject instanceof OTDataObject) {
database.removeReference(this, (OTDataObject) oldObject);
} else if (oldObject instanceof OTID) {
database.removeReference(this.globalId, (OTID) oldObject);
}
if (resource instanceof OTDataObject) {
database.recordReference(this, (OTDataObject) resource, key);
} else if (resource instanceof OTID) {
try {
database.recordReference(this.globalId, (OTID) resource, key);
} catch (Exception e) {
// TODO do we care?
}
}
}
if(!markDirty) return true;
if((oldObject == null && resource == null) ||
(oldObject != null &&
oldObject.equals(resource))) {
// the object wasn't really modified
return false;
} else {
updateModifiedTime();
}
return true;
}
/* (non-Javadoc)
* @see org.concord.otrunk.OTDataObject#getResource(java.lang.String)
*/
public Object getResource(String key)
{
return resources.get(key);
}
Object getResourceWithSuffix(String key)
{
int suffixStartIndex = key.indexOf('[');
if(suffixStartIndex == -1){
return getResource(key);
}
int suffixEndIndex = key.indexOf(']');
if(suffixEndIndex == -1){
throw new IllegalStateException("invalid key format: " + key);
}
String property = key.substring(0, suffixStartIndex);
Object resource = getResource(property);
if(resource instanceof XMLDataList){
XMLDataList list = (XMLDataList) resource;
String indexStr = key.substring(suffixStartIndex+1, suffixEndIndex);
int index = Integer.parseInt(indexStr);
if(index < 0 || index >= list.size()){
return null;
}
return list.get(index);
} else if(resource instanceof XMLDataMap){
XMLDataMap map = (XMLDataMap) resource;
String mapKey = key.substring(suffixStartIndex+2, suffixEndIndex-1);
return map.get(mapKey);
} else {
throw new IllegalStateException("unknown resource type for key: " + key);
}
}
public void setResourceInfo(String key, XMLReferenceInfo info)
{
referenceInfoMap.put(key, info);
}
public XMLReferenceInfo getReferenceInfo(String key)
{
return referenceInfoMap.get(key);
}
/* (non-Javadoc)
* @see org.concord.otrunk.OTDataObject#getResourceKeys()
*/
public String[] getResourceKeys()
{
Set<String> keySet = resources.keySet();
return keySet.toArray(new String [keySet.size()]);
}
/* (non-Javadoc)
* @see org.concord.otrunk.OTDataObject#getCurrentRevision()
*/
public OTObjectRevision getCurrentRevision()
{
return null;
}
void updateModifiedTime()
{
// update our revision
// tell the database that it is "dirty" and needs to
// be synced
database.setDirty(true);
}
public Collection<Entry<String, Object>> getResourceEntries()
{
return resources.entrySet();
}
/**
* @return Returns the localId.
*/
public String getLocalId()
{
return localId;
}
/**
* @param localId The localId to set.
*/
public void setLocalId(String localId)
{
this.localId = localId;
}
@SuppressWarnings("unchecked")
public <T extends OTDataCollection> T getResourceCollection(String key,
Class<T> collectionClass)
{
Object listObj = getResource(key);
if(collectionClass.isInstance(listObj)) {
return (T)listObj;
}
OTDataCollection collection = null;
if(collectionClass.equals(OTDataList.class)) {
collection = new XMLDataList(this);
} else if(collectionClass.equals(OTDataMap.class)) {
collection = new XMLDataMap(this);
}
if(collection != null) {
// don't mark the object as dirty until the collection has actually be modified
setResource(key, collection, false);
}
return (T)collection;
}
public OTDataObjectType getType()
{
return type;
}
public XMLDataObject getContainer()
{
return container;
}
public void setContainer(XMLDataObject container)
{
this.container = container;
}
public String getContainerResourceKey()
{
return containerResourceKey;
}
public void setContainerResourceKey(String containerResourceKey)
{
this.containerResourceKey = containerResourceKey;
}
public URL getCodebase()
{
return database.getContextURL();
}
public boolean isPreserveUUID()
{
return preserveUUID;
}
public void setPreserveUUID(boolean preserveUUID)
{
this.preserveUUID = preserveUUID;
}
public boolean containsKey(String key)
{
return resources.containsKey(key);
}
public void setSaveNulls(boolean flag)
{
saveNulls = flag;
}
public boolean getSaveNulls()
{
return saveNulls;
}
public void nullifyElement() {
this.element = null;
}
}