package org.concord.otrunk.overlay;
import java.util.ArrayList;
import org.concord.framework.otrunk.OTID;
import org.concord.framework.otrunk.OTResourceList;
import org.concord.framework.otrunk.OTResourceMap;
import org.concord.otrunk.OTObjectServiceImpl;
import org.concord.otrunk.datamodel.OTDataObject;
import org.concord.otrunk.datamodel.OTDataPropertyReference;
import org.concord.otrunk.datamodel.OTDatabase;
public class OverlayImpl
implements Overlay
{
private OTOverlay otOverlay;
/**
* This is the database of the overlay itself. This is NOT the composite database that
* presents the combination of multiple overlays.
*/
private OTDatabase overlayDb;
private ArrayList<OverlayListener> listeners = new ArrayList<OverlayListener>();
public OverlayImpl(OTOverlay otOverlay)
{
this.otOverlay = otOverlay;
OTObjectServiceImpl objServiceImpl =
(OTObjectServiceImpl)otOverlay.getOTObjectService();
overlayDb = objServiceImpl.getCreationDb();
}
public boolean contains(OTID id)
{
// If we haven't done it before, we should go through all the delta objects at this
// point and track down any non delta objects they are creating.
// All of the values in the otOverlays map are delta objects.
// If we could check containment then any reference to a child element
// which was contained by a delta object would be a non delta object
// if we can't use containment, then we can check if the reference object is in our
// map, but we have no way to know if the object is part of the overlay or not.
// Because we don't have explicit containment yet, (maybe now is the time)
// we need to make a list in the OTObject to track non delta objects.
// TODO Auto-generated method stub
OTResourceList nonDeltaObjects = otOverlay.getNonDeltaObjects();
for(int i=0; i<nonDeltaObjects.size(); i++){
OTID nonDeltaId = (OTID)nonDeltaObjects.get(i);
if(nonDeltaId.equals(id)){
return true;
}
}
return false;
}
public OTDataObject createDeltaObject(OTDataObject baseObject)
{
try {
OTDataObject stateObject = overlayDb.createDataObject(baseObject.getType());
OTResourceMap deltaObjectMap = otOverlay.getDeltaObjectMap();
deltaObjectMap.put(baseObject.getGlobalId().toExternalForm(),
stateObject.getGlobalId());
for(int i=0; i<listeners.size(); i++){
(listeners.get(i)).newDeltaObject(this, baseObject);
}
return stateObject;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public OTDataObject getDeltaObject(OTDataObject baseObject)
{
OTResourceMap detalObjectMap = otOverlay.getDeltaObjectMap();
OTID deltaObjectId = (OTID)detalObjectMap.get(baseObject.getGlobalId().toExternalForm());
// System.err.println("OTReferenceMap: requesting stateObject for: " +
// template.getGlobalId());
if(deltaObjectId == null) {
return null;
}
try {
return overlayDb.getOTDataObject(null, deltaObjectId);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public OTDatabase getOverlayDatabase()
{
return overlayDb;
}
public void registerNonDeltaObject(OTDataObject childObject)
{
OTID id = childObject.getGlobalId();
OTResourceList nonDeltaObjects = otOverlay.getNonDeltaObjects();
for(int i=0; i<nonDeltaObjects.size(); i++){
OTID nonDeltaId = (OTID)nonDeltaObjects.get(i);
if(nonDeltaId.equals(id)){
// This object is already registered.
return;
}
}
nonDeltaObjects.add(id);
}
public void addOverlayListener(OverlayListener listener)
{
listeners.add(listener);
}
public void removeOverlayListener(OverlayListener listener)
{
listeners.remove(listener);
}
public synchronized void pruneNonDeltaObjects() {
// FIXME There's still a problem where objects can be orphaned but not pruned when the overlay is saved, the current session is ended, and then overlay is loaded in a new session and saved
// we need to do this process repeatedly, since pruning one object may end up freeing up another object to be pruned in a later loop
boolean objectsWerePruned = false;
do {
ArrayList<OTID> objectsToPrune = new ArrayList<OTID>();
OTResourceList nonDeltaObjects = otOverlay.getNonDeltaObjects();
for (int i = 0; i < nonDeltaObjects.size(); i++) {
OTID nonDeltaId = (OTID)nonDeltaObjects.get(i);
ArrayList<OTDataPropertyReference> incomingReferences = overlayDb.getIncomingReferences(nonDeltaId);
if (incomingReferences.size() == 1) {
System.out.println("Object " + nonDeltaId + " was only referenced in one place: " + incomingReferences.get(0).getSource() + ":" + incomingReferences.get(0).getProperty());
objectsToPrune.add(nonDeltaId);
}
}
objectsWerePruned = (objectsToPrune.size() > 0);
for (OTID id : objectsToPrune) {
nonDeltaObjects.remove(id);
ArrayList<OTDataPropertyReference> outgoingReferences = overlayDb.getOutgoingReferences(id);
if (outgoingReferences != null) {
ArrayList<OTID> refsToRemove = new ArrayList<OTID>();
for (OTDataPropertyReference ref : outgoingReferences) {
refsToRemove.add(ref.getDest());
}
for (OTID child : refsToRemove) {
overlayDb.removeReference(id, child);
}
}
}
} while(objectsWerePruned);
}
}