Package org.persvr.data

Source Code of org.persvr.data.PersistableObject$PersistentInitializer

package org.persvr.data;

import java.beans.PropertyChangeListener;
import java.lang.ref.SoftReference;
import java.security.Principal;
import java.security.acl.Acl;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.Map.Entry;

import org.mozilla.javascript.BaseFunction;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeFunction;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.UniqueTag;
import org.persvr.data.DataSourceManager.SourceInfo;
import org.persvr.data.Transaction.ChangeUpdate;
import org.persvr.datasource.ChangeableData;
import org.persvr.datasource.DataSource;
import org.persvr.datasource.ListDataSource;
import org.persvr.datasource.NewObjectPersister;
import org.persvr.datasource.PersistableInitializer;
import org.persvr.datasource.ReferenceAwareDataSource;
import org.persvr.datasource.WritableDataSource;
import org.persvr.javascript.PersevereContextFactory;
import org.persvr.security.PermissionLevel;
import org.persvr.security.UserSecurity;

/**
* This class adds the capability to persist data for Rhino Scriptables
* @author Kris Zyp
*
*/
public class PersistableObject extends NativeObject implements ObservablePersistable {
  private static final long serialVersionUID = 1509175903523676521L;
  public Object noCheckGet(String key) {
    Object value = super.get(key,this);
        try {
      while (value instanceof TargetRetriever)
        value = ((TargetRetriever)value).getTarget();
    } catch (ObjectNotFoundException e) {
      value = null;
    }
        if (value instanceof Date) // TODO: Do we need to find a way to make this return the same date each time, I don't know if it is even possible with objects being collected
          return ScriptRuntime.newObject(PersevereContextFactory.getContext(), GlobalData.getGlobalScope(), "Date", new Object[] {((Date)value).getTime()});
        return value;
  }
  static ThreadLocal<Object> securityEnabled = new InheritableThreadLocal<Object>();
  public static void enableSecurity(boolean enable){
    securityEnabled.set(enable ? SECURITY_ENABLED_OBJECT : null);
  }
  public static boolean isSecurityEnabled(){
    return securityEnabled.get() != null;
  }
  static Object SECURITY_ENABLED_OBJECT = new Object();
  void noCheckSet(String key,Object value) {
    if (value == Scriptable.NOT_FOUND)
      super.delete(key);
    else
      super.put(key,this,value);
  }
  public PersistableObject() {

  }
  protected ObjectId id;
  Object parent;
  Persistable schema;

  long lastUpdated;
  private static final int CACHE_TIME = 200;
  private void updateObject(){
    //TODO: We may be able to optimize this better by using a HashMap of
    //  id to last updated so non-changeable data doesn't increase memory size.
    //  Also we might have a timer that updates a static variable for faster
    //  access to the current time in millis.
    if(id.source instanceof ChangeableData){
      long currentTime = System.currentTimeMillis();
      if(currentTime - lastUpdated > CACHE_TIME){
        lastUpdated = currentTime;
        if (((ChangeableData)id.source).doesObjectNeedUpdating(id.subObjectId))
          mapPersistent(id);
      }
    }
  }
  public ObjectId getId() {
    if (id == null)
      return (id = new NewObjectId(this));
    return id;
  }
    //TODO: It would be nice if this was package protected to Client
    public Object getCoreValue(String name) {
        Object value = super.get(name, this);
        if(value instanceof TransactionValue){
          value = ((TransactionValue)value).getTarget();
        }
        return value;
    }
    public Object get(String name) {
      return convertToDateJavaDate(getProperty(this, name));
    }
    private void recordPropertyRead(String name) {
      Map<ObjectId, Set<String>> readSet = readSets.get();
      if (readSet != null) {
        Set<String> propertySet = readSet.get(id);
        if (propertySet == null) {
          readSet.put(id, propertySet = new HashSet<String>());
        }
        propertySet.add(name);
      }

    }
    @Override
  public Object get(String name, Scriptable start) {
      boolean securityCheck = false;
      if (id != null && id.source != null) {
        if("id".equals(name)){
          return id.toString();
        }
        if(securityEnabled.get() != null){
          checkSecurity(this, PermissionLevel.READ_LEVEL.level);
          securityCheck = true;
        }
        updateObject();
      }

        Object value = super.get(name,start);
    if(securityCheck && value != Scriptable.NOT_FOUND && (getAttributes(name) & ScriptableObject.DONTENUM) == ScriptableObject.DONTENUM)
      checkSecurity(this, PermissionLevel.WRITE_LEVEL.level);

        try {
      while (value instanceof TargetRetriever)
        value = ((TargetRetriever)value).getTarget();
    } catch (ObjectNotFoundException e) {
      value = null;
    }
        if (value instanceof Date) // TODO: Do we need to find a way to make this return the same date each time, I don't know if it is even possible with objects being collected
          return ScriptRuntime.newObject(PersevereContextFactory.getContext(), GlobalData.getGlobalScope(), "Date", new Object[] {((Date)value).getTime()});
        else if(value instanceof Method)
        ((Method)value).setName(name);
        return value;
    }
    Object asTransactionValue(String name, Object oldValue, Object value) {
    if (oldValue instanceof TransactionValue){
      ((TransactionValue)oldValue).setValue(this, name, value);
      value = oldValue;
    }
    else
      return new TransactionValue(this,name,oldValue,value);
    return value;
    }
    @Override
  public void put(int index, Scriptable start, Object obj) {
      if(obj instanceof NativeFunction){
        obj = new Method((BaseFunction)obj,"" + index);
      }
      if (id != null && id.source != null && (obj == null ? get(index,start) != null : !obj.equals(get(index,start)))) {
        checkPut(index+"",obj,true);
        obj = asTransactionValue(index + "",super.get(index,this),obj);
      }
    super.put(index,start,obj);
    commitIfImmediate();
  }


  static void checkPut(Persistable obj, int index, Object value) {
    if(securityEnabled.get() != null){
      checkSecurity(obj, PermissionLevel.WRITE_LEVEL.level);
    }

      enforceObjectChange(obj,"items", value, true);

  }

  public final static Object ADDITION = new Object();
  static int getAttributes(Persistable object, String key){
    try {
      return ((PersistableObject)object).getAttributes(Integer.parseInt(key));
    } catch (NumberFormatException e) {
      return ((PersistableObject)object).getAttributes(key);
    }
  }
  public static void commitPut(Entry<TransactionValue,ChangeUpdate> changeEntry, Transaction transaction) throws Exception {
    boolean wasSecurityEnabled = PersistableObject.isSecurityEnabled();
    // turn off security so no checks are made while we are persisting data
    try{
      PersistableObject.enableSecurity(false);
      TransactionValue transValue = changeEntry.getKey();
      ChangeUpdate change = changeEntry.getValue();
      Persistable target = change.target;
      Object value = transValue.values.get(transaction);
        Object sourceValue = value;
        Object oldValue = transValue.values.get(Transaction.OUTSIDE);
        boolean hadProperty = oldValue != Scriptable.NOT_FOUND;
        if (sourceValue instanceof PersistableObject) {
          sourceValue = ((PersistableObject) sourceValue).getId();
        }
        sourceValue = convertToDateJavaDate(sourceValue);
      Identification id = target.getId();
      if (id instanceof ObjectId && id.source != null) { // it should always be persisted right?
        if (change.key != null) {
          DataSource initialSource = id.source;
          WritableDataSource source;
          String subObjectId = id.subObjectId;
          if (subObjectId == null) {
            subObjectId = initialSource.getId();
            source = DataSourceManager.metaClassSource;
          }
          else {
            source = (WritableDataSource) initialSource;
          }
          Transaction.addAffectedSource(source);
          String key =change.key;

          if (hadProperty) {
            if (value == Scriptable.NOT_FOUND)
              source.recordPropertyRemoval(subObjectId, key);
            else
              source.recordPropertyChange(subObjectId, key, sourceValue, getAttributes(target, key));

          }
          else {

            source.recordPropertyAddition(subObjectId, key, sourceValue, getAttributes(target, key));
          }
        }
        else {
          Transaction.addAffectedSource((WritableDataSource)id.source);
          if (sourceValue == Scriptable.NOT_FOUND) {
            ((WritableDataSource) id.source).recordDelete(id.subObjectId);
            ((ObjectId)id).removeFromCache();
            // we can't do this or the data source won't know what the id of the object is
//            if(target instanceof PersistableObject)
  //            ((PersistableObject) target).id = null; // at least remove the id in case it is accessed again
          }
          else
            ((ListDataSource)id.source).recordList(id.subObjectId, (List)sourceValue);

        }
      }
      if(target instanceof PersistableObject)
        ((PersistableObject)target).lastModified = Transaction.currentTransaction().getTransactionTime();
      else
        ((PersistableArray)target).lastModified = Transaction.currentTransaction().getTransactionTime();
    }
    finally{
      PersistableObject.enableSecurity(wasSecurityEnabled);
    }
  }

  public Persistable getSchema() {
    return schema == null ?
        (id != null && id.source != null ?
            schema = ObjectId.idForObject(DataSourceManager.metaClassSource, id.source.getId()).getTarget() :
            null) :
          schema;
  }


  /**
   *
   * @param name
   * @param obj
   * @param alwaysPersist
   * @return true if it should be persisted
   */
  static Object enforceObjectChange(Persistable persistent, Object name, Object obj,boolean hadProperty) {
      return PersistableClass.enforceSchemaForProperty(persistent.getSchema(),persistent, name,obj,hadProperty, false, hadProperty);
  }
    protected Object checkPut(String name, Object obj,boolean alwaysPersist) {
    if(securityEnabled.get() != null){
      checkSecurity(this, PermissionLevel.WRITE_LEVEL.level);
    }

      boolean hadProperty = false;
      try {
        hadProperty = has(name,this);
      }
      catch (Exception e) {
        e.printStackTrace();
      }

      return enforceObjectChange(this,name, obj, hadProperty);
    }

  @Override
  public void put(String name, Scriptable start, Object obj) {
      if(obj instanceof NativeFunction){
        obj = new Method((BaseFunction)obj,name);
      }
      if (id != null && id.source != null){// if it is a real persistent object
      if(start == this // only do this if we are at the end of the chain
        ) {
          obj = checkPut(name,obj,false);
        if(id.isPersisted()){
            Object oldValue = super.get(name,start);
            if (oldValue instanceof TransactionValue){
              ((TransactionValue)oldValue).setValue(this, name, obj);
              obj = oldValue;
            }
            else
              obj = new TransactionValue(this,name,oldValue,obj);
        }
      }
    }
    else{
      if("id".equals(name)){
        if(getId().isPersisted()){
          throw ScriptRuntime.constructError("TypeError", "Can not change the id of a persisted object");
        }
        getId().subObjectId = ScriptRuntime.toString(obj);
      }
    }

      if(obj instanceof GetterSetterCombo){
        setGetterOrSetter(name, 0, ((GetterSetterCombo)obj).getter, false);
        setGetterOrSetter(name, 0, ((GetterSetterCombo)obj).setter, true);
      }
      else
        super.put(name, start, obj);
      if(name.charAt(0)==':') // this is how we can signify a dont-enum
        setAttributes(name, ScriptableObject.DONTENUM);

    commitIfImmediate();
    }

    public Object set(String name, Object value) {
      Object sourceValue = value;
      if(value instanceof NativeFunction){
        sourceValue = value = new Method((BaseFunction)value,name);
      }
      if(getPrototype() != null && ScriptableObject.hasProperty(getPrototype(), name) &&
          ScriptableObject.getProperty(getPrototype(), name) instanceof Method
          && !(this instanceof SchemaObject)){
        throw ScriptRuntime.constructError("TypeError", "Can not set a value in an instance property that overrides a method");
      }

      if (id != null && id.source != null){
        Object oldValue = get(name,this);
        if(oldValue == UniqueTag.NOT_FOUND)
          oldValue = Undefined.instance;
        if ((value== null ? oldValue != null : !ScriptRuntime.shallowEq(oldValue, value))) {
        value = checkPut(name,value,true);
        if(id.isPersisted())
          sourceValue = asTransactionValue(name,super.get(name,this),value);
        }
      }else{
      if("id".equals(name)){
        if(getId().isPersisted()){
          throw ScriptRuntime.constructError("TypeError", "Can not change the id of a persisted object");
        }
        getId().subObjectId = ScriptRuntime.toString(value);
      }

      }
      try {
        char firstChar = name.charAt(0);
        if(firstChar <= '9' && firstChar >= '0') { // peformance guard
          int index = Integer.parseInt(name);
          super.put(index, this, sourceValue);
        }
        else{
          super.put(name, this, sourceValue);
        }
      }
      catch (NumberFormatException e) { // TODO: Maybe we should do a regex match so we dont' have to throw an exception
          super.put(name, this, sourceValue);
      }
      commitIfImmediate();
    return value;
    }

  static void commitIfImmediate() {
    Transaction.currentTransaction().commitIfImmediate();
  }
    @Override
    public void delete(String name) {

      if (id != null && id.source != null) {
        if(securityEnabled.get() != null){
          checkSecurity(this, PermissionLevel.WRITE_LEVEL.level);
        }

          boolean hadProperty = false;
          try {
            hadProperty = has(name,this);
          }
          catch (Exception e) {
            e.printStackTrace();
          }
          enforceObjectChange(this,name, Scriptable.NOT_FOUND, hadProperty);
        Object transValue = super.get(name,this);
        if (transValue instanceof TransactionValue)
          ((TransactionValue)transValue).setValue(this, name, Scriptable.NOT_FOUND);
        else
          transValue = new TransactionValue(this,name,transValue, Scriptable.NOT_FOUND);
        super.put(name, this, transValue);
        //recordDelete(name);
      }
      else
        super.delete(name);
//      lastModified = getTransactionTime();
    }
    private void recordDelete(String name) {
      try {
        Identification id = getId();
      if (id instanceof ObjectId && id.source != null) {
        ((WritableDataSource) id.source).recordPropertyRemoval(id.subObjectId, name);
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    }
    static Object convertToIdIfNeeded(Object object) {
      if (object instanceof Persistable) {
        // TODO: Handle the case of data object that is not a persistent object
        return ((PersistableObject)object).getId();
      }
      return object;
    }

    protected void initializeProperty(String name, Object value) {
      if(isSealed())
        return;
      if(value instanceof NativeFunction){
        value = new Method((BaseFunction)value,name);
      }

      super.put(name, this, value);
      if(name.charAt(0) == ':')
        setAttributes(name, ScriptableObject.DONTENUM);
    }
/*    private static Scriptable getObjectPrototype() {
      return ScriptableObject.getClassPrototype(GlobalData.getGlobalScope(),"Object");
  }*/
    static private Scriptable arrayPrototype = null;
    private static Scriptable getArrayPrototype() {
      return arrayPrototype == null ? arrayPrototype = ScriptableObject.getClassPrototype(GlobalData.getGlobalScope(),"Array") : arrayPrototype;
  }
    static private Scriptable queryPrototype = null;
    private static Scriptable getQueryPrototype() {
      return queryPrototype == null ? queryPrototype = ScriptableObject.getClassPrototype(GlobalData.getGlobalScope(),"Query") : queryPrototype;
  }
    public static Object convertToDateJavaDate(Object value){
    if (value instanceof Scriptable && "Date".equals(((Scriptable)value).getClassName())) {
      // it is a date
      double time = (Double) ((Function) ScriptableObject.getProperty((Scriptable)value,"getTime")).call(PersevereContextFactory.getContext(), GlobalData.getGlobalScope(), (Scriptable)value, new Object[]{});
      value = new Date((long) time);
    }
    return value;

    }
    static void persistNewObject(final Persistable newObject,NewObjectPersister newObjectPersister) {
      try {
      if (newObject instanceof List)
        newObjectPersister.initializeAsList((List) newObject);
      //Scriptable prototype = newObject.getPrototype();
//      if (prototype != null && prototype != getClassPrototype(GlobalData.getGlobalScope(), "Object")
//          && prototype != getClassPrototype(GlobalData.getGlobalScope(), "Array")
//          && !(newObject instanceof Function))
//        newObjectPersister.recordProperty(GlobalData.BASIS_FIELD, prototype);
      if (!(newObject instanceof PersistableClass))
        for (Map.Entry<String, Object> entry: newObject.entrySet(1))
          newObjectPersister.recordProperty(entry.getKey(), convertToDateJavaDate(entry.getValue()));

      setParent(newObject,newObjectPersister.getParent());
      newObjectPersister.finished();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    }
    private static void setId(Persistable target, ObjectId id) {
      if (target instanceof PersistableObject) {
        ((PersistableObject)target).id = id;
      }
      else {
        ((PersistableArray)target).id = id;

      }
    }
    private static void setParent(Persistable target, ObjectId parent) {
      if (target instanceof PersistableObject) {
      ((PersistableObject)target).parent = parent;
      }
      else {
      ((PersistableArray)target).parent = parent;
      }
    }
    public static PersistableObject initObject(DataSource source) {
    PersistableObject result;
      if(source == null){
      result = new PersistableObject();
        ScriptRuntime.setObjectProtoAndParent((ScriptableObject) result, GlobalData.getGlobalScope());
        return result;
      }
    SourceInfo info = DataSourceManager.getObjectsClass(source);
    if (info.objectsClass !=null)
      try {
        result = (PersistableObject) info.objectsClass.newInstance();
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    else
      result = new PersistableObject();
        ScriptRuntime.setObjectProtoAndParent((ScriptableObject) result, GlobalData.getGlobalScope());
        Scriptable proto = info.schema.getPrototypeProperty();
        if (proto != null)
          result.setPrototype(proto);
        result.schema = info.schema;
        return result;
    }
    public static PersistableArray initArray(DataSource source) {
      SourceInfo info = null;
      //if (basis != null)
        //clazz = Templates.getClassForObject(basis);
      PersistableArray result;
      if (source != null && (info = DataSourceManager.getObjectsClass(source)).objectsClass !=null &&
          PersistableArray.class.isAssignableFrom(info.objectsClass))
        try {
          result =  (PersistableArray) info.objectsClass.getConstructor(Object[].class).newInstance(0);
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
      else
        result = new PersistableArray(0);
        ScriptRuntime.setObjectProtoAndParent((ScriptableObject) result, GlobalData.getGlobalScope());
        return result;
    }
    private boolean clearSlotsIfClean(){
    for(Object key : getIds()){
      if (super.get(key.toString(),this) instanceof TransactionValue)
        return false;
    }
/*    for(Object key : getIds()){ // technically we should be deleting keys in case an updated representation from the server has keys removed, but removing them here causes concurrency issues
      super.delete(key.toString());
    }*/
    return true;
    }
    public static void setVersion(Persistable object, Version version) {
    if(object instanceof PersistableObject)
      ((PersistableObject)object).version = version;
    else
      ((PersistableArray)object).version = version;

    }
    private static class PersistentInitializer implements PersistableInitializer {

    public void setLastModified(Date lastModified) {
      if(newObject instanceof PersistableObject)
        ((PersistableObject)newObject).lastModified = lastModified;
      else
        ((PersistableArray)newObject).lastModified = lastModified;
    }

    public void setVersion(Version version) {
      PersistableObject.setVersion(newObject, version);
    }
    Persistable newObject;
      ObjectId id;
      PersistentInitializer() {
      }
      PersistentInitializer(ObjectId id) {
        this.id = id;
        // reuse the existing object if it is available
        if(id.targetRef != null)
          newObject = id.targetRef.get();
      }

    public void initializeList(Collection sourceCollection) {
      if(!(newObject instanceof PersistableList))
        newObject = initArray("".equals(id.subObjectId) ? null : id.source);
      if(sourceCollection != null)
        ((PersistableList) newObject).initSourceCollection(sourceCollection);
      setId(newObject,id);
    }
/*    private Object handleChildObjectInitialize(Object value) {
      if (value instanceof ObjectWithInitialization) {
        ObjectId childId = ((ObjectWithInitialization)value).getId();
        try {
          DataObjectInitializer childInitializer = new PersistentInitializer(childId);
          ((ObjectWithInitialization)value).mapObject(childInitializer);
          childId.targetRef = new SoftReference(childInitializer.getInitializingObject());
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
        value = childId;
      }
      return value;
    }*/
    public void setProperty(String name, Object value, int attributes) {
      try {
        if (newObject == null) {
          newObject = initObject(id.source);
          setId(newObject,id);
        }
        ((PersistableObject)newObject).initializeProperty(name,value);//handleChildObjectInitialize(value)
        if(value instanceof Persistable){
          if (((Persistable)value).getSchema() == null){
            Persistable schema = ((PersistableObject)newObject).getSchema();
            Object properties;
            if(schema != null){
              properties = schema.get("properties");
              if(properties instanceof Persistable){
                Object valueSchema = ((Persistable)properties).get(name);
                if(valueSchema instanceof Persistable){
                  if(value instanceof PersistableObject)
                    ((PersistableObject)value).schema = (Persistable) valueSchema;
                  else
                    ((PersistableArray)value).schema = (Persistable) valueSchema;
                }
              }
            }
          }
        }
        if(attributes != 0)
          ((PersistableObject)newObject).setAttributes(name, attributes);
      } catch (RuntimeException e) {
        e.printStackTrace();
      }
    }
    public void setProperty(String name, Object value) {
      setProperty(name, value, 0);
    }

    public void setParent(ObjectId objectToInheritFrom) {
      if (newObject == null) {
        newObject = initObject(id.source);
        setId(newObject,id);
      }
        if (objectToInheritFrom == id && !(newObject instanceof Acl))
          return; //throw new RuntimeException("Can not set an acl id to be the same as the object id unless the object is an acl");
        PersistableObject.setParent(newObject,objectToInheritFrom);
    }
    public Persistable getInitializingObject() {
      if (newObject == null){
        if(id.targetRef != null)
          newObject = id.targetRef.get();
        if (newObject == null)
          newObject = initObject(id.source);
      }
      setId(newObject,id);
      id.targetRef = new SoftReference<Persistable>(newObject);
      return newObject;
    }
    public void finished() {

    }
    };
    /**
     * @see DataSourceHelper.initializeObject()
     * @param id
     * @return
     */
    static PersistableInitializer initializeObject() {
      return new PersistentInitializer();

    }
    /**
     * @see DataSourceHelper.initializeObject(id)
     * @param id
     * @return
     */
    static PersistableInitializer initializeObject(final ObjectId id) {
    PersistentInitializer initializer = new PersistentInitializer(id) {
        @Override
      public void finished() {
          getInitializingObject();
        }
      };
      checkForExistingDirty(initializer);
      return initializer;
    }
    public static boolean checkForExistingDirty(PersistableInitializer initializer){
      PersistentInitializer init = (PersistentInitializer) initializer;
      ObjectId id = init.id;
      if(id.subObjectId != null && id.subObjectId.startsWith("s$"))
        return true;
    if (id.targetRef != null){
      init.newObject = (Persistable) id.targetRef.get(); // in case it is already initialized
      if(init.newObject != null){
        if(init.newObject instanceof PersistableObject && !((PersistableObject)init.newObject).isSealed()){
          return !((PersistableObject)init.newObject).clearSlotsIfClean();
        }
      }
    }
    return false;
    }
    static Map<Persistable, Scriptable> schemasInstancesPrototype = new HashMap();;
    static Scriptable instancesPrototypeForSchema(Persistable schema) {
      Object instancesPrototype = schemasInstancesPrototype.get(schema);
      if (instancesPrototype == null) {
      instancesPrototype = schema.noCheckGet("instancesPrototype");
      Object superType = PersistableClass.getSuperType(schema);
      Scriptable nextPrototype = superType instanceof Persistable ? instancesPrototypeForSchema((Persistable) superType) :
        getQueryPrototype();
      if (instancesPrototype instanceof Persistable) {
        ((Persistable) instancesPrototype).setPrototype(nextPrototype);
        schemasInstancesPrototype.put(schema, (Scriptable) instancesPrototype);
      }
      else {
        return nextPrototype;
      }
      }
      return (Scriptable) instancesPrototype;
    }
    static Persistable mapPersistent(final ObjectId id) {
      PersistentInitializer initializer;

      initializer = new PersistentInitializer(id);
      if (checkForExistingDirty(initializer))
      //this means we are an existing dirty object , so we shouldn't update it
        return initializer.newObject;
      try {
          if (id instanceof Query){
            Collection queryResults = id.source.query((Query)id);
            if(queryResults instanceof List){
              initializer.newObject = new QueryArray(queryResults);
              initializer.newObject.setPrototype(instancesPrototypeForSchema(
                  DataSourceManager.getObjectsClass(id.source).schema));
            }
            else
              initializer.initializeList(queryResults);
/*            Object superType = PersistableClass.getSuperType(ObjectId.idForObject(DataSourceManager.metaClassSource, id.source.getId()).getTarget());
            if(superType instanceof Persistable)
              initializer.setParent(ObjectId.idForString(((Persistable)superType).getId().subObjectId + "/"));
            else*/
              initializer.setParent(DataSourceManager.getRootObject().getId());
          }
          else {
          id.source.mapObject(initializer,id.subObjectId);
          }
      } catch (QueryCantBeHandled qe) {
        throw qe;
      } catch(ObjectNotFoundException oe) {
        throw oe;
    } catch (Exception e) {
      //e.printStackTrace();
      Persistable newObject = initializer.getInitializingObject();
      setId(newObject,null); // do this so we can set the error without trying to persist it
      if(newObject instanceof PersistableObject)
        ((PersistableObject)newObject).initializeProperty("error", e.toString());
      else
        ((PersistableArray)newObject).add(e.toString());
      setId(newObject,id);
    }
    Persistable newObject = initializer.getInitializingObject();
      if(newObject instanceof PersistableObject){
        ((PersistableObject)newObject).lastUpdated = System.currentTimeMillis();
      } else if(newObject instanceof PersistableArray){
        ((PersistableArray)newObject).lastUpdated = System.currentTimeMillis();
      }
        return newObject;
    }
/*   static class PropertyChangeContext {
      User user;
      PropertyChangeListener listener;
      Map<String, Map<String,
    public PropertyChangeContext(User user, PropertyChangeListener listener) {
      super();
      this.user = user;
      this.listener = listener;
    }
    @Override
    public boolean equals(Object obj) {
      return listener.equals(obj);
    }
    @Override
    public int hashCode() {
      return listener.hashCode();
    }
    }*/
    static Map<PropertyChangeSetListener,Map<ObjectId, Set<String>>> watchSets = new WeakHashMap<PropertyChangeSetListener,Map<ObjectId, Set<String>>>();
    static InheritableThreadLocal<Map<ObjectId, Set<String>>> readSets = new InheritableThreadLocal<Map<ObjectId,Set<String>>>();
    public static Map<ObjectId, Set<String>> startReadSet() {
      Map<ObjectId, Set<String>> readSet;
    readSets.set(readSet = new HashMap<ObjectId, Set<String>>());//readSets.get()
      return readSet;
    }
    public static Map<ObjectId, Set<String>> getReadSet() {
      return readSets.get();
    }
  /**
   * This is used to listen for changes in an object that have been read in this transaction. You MUST keep a reference
   * to the listener as long as you wish the it to listen. Once the listener doesn't have a reference
   * it can be reclaimed and it will no longer listen.
   * @param listener
   * @return
   */
    public static void addListener(PropertyChangeSetListener listener) {
        Map<ObjectId, Set<String>> readSet = readSets.get();
    Map<ObjectId, Set<String>> watchSet = watchSets.get(listener);
    if (watchSet == null) {
      synchronized (watchSets) {
        watchSets.put(listener,
            watchSet = new HashMap<ObjectId, Set<String>>());
      }
    }
    for (Map.Entry<ObjectId, Set<String>> entry : readSet.entrySet()) {
      ObjectId key = entry.getKey();
      Set<String> value = entry.getValue();
      if (watchSet.containsKey(key) && value != FullSet.instance)
        watchSet.get(key).addAll(value);
      else {
        if (watchSet.containsKey(key) && key instanceof Query
            && ((Query) key).conditionFunction != null) {

          ObjectId oldQuery = null;
          // find the old query (and it's conditionFunction)
          for (Map.Entry<ObjectId, Set<String>> watchEntry : watchSet
              .entrySet()) {
            oldQuery = watchEntry.getKey();
            if (oldQuery.equals(key)) {
              break;
            }
          }
          final Function newFunction = ((Query) key).conditionFunction;
          final Function oldFunction = ((Query) oldQuery).conditionFunction;
          if (oldFunction != null) {
            // create a new function that combines the other two
            ((Query) oldQuery).conditionFunction = new BaseFunction() {
              public Object call(org.mozilla.javascript.Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
                return ScriptRuntime.toBoolean(newFunction.call(cx,  scope, thisObj, args)) ||
                  ScriptRuntime.toBoolean(oldFunction.call(cx, scope, thisObj, args));
              }
            };
          }
        }
        watchSet.put(key, value);
      }
    }
    }
    public static void removeListener(PropertyChangeListener listener) {
    synchronized(watchSets){
      watchSets.remove(listener);
    }
    }
    /**
     * Stops listening for the
     * @param listener
     */
    public static Map<ObjectId, Set<String>> getWatchSet(PropertyChangeSetListener listener) {
      return watchSets.get(listener);
    }





  public static void resetComputedPermissions() {
    computedPermissions = new WeakHashMap<Principal, Map<Persistable,PermissionLevel>>();
  }
    protected static Map<Principal,Map<Persistable,PermissionLevel>> computedPermissions = new WeakHashMap<Principal, Map<Persistable,PermissionLevel>>();
    static class PermissionChangeListener implements PropertyChangeSetListener {
    public void propertyChange(List<ObservedCall> evts) {
      resetComputedPermissions();
    }
    }

  public static Map<String,PermissionLevel> permissionNames = new HashMap<String,PermissionLevel>();

    public int getAccessLevel() {
      return checkSecurity(this,-1);
    }
    public static final int BROWSE = 0// TODO: These constants should be combined with PermissionLevel
    public static final int LIMITED = 1;
    public static final int READ = 2;
    public static final int WRITE = 5;
    public static final int EXECUTE = 3;
    public static final int APPEND = 4;
    public static final int ACCESS_PERMISSION_LEVEL = 6;

    // cached public permission for maximum speed
    //int universalPermissionLevel = -1;
    public static int checkSecurity(Persistable data,int level) {
/*      if (level != -1 && data.getUniversalPermissionLevel() >= level)
        return data.getUniversalPermissionLevel();
/*      Acl acl = data.getAcl();
        if (acl == null) // indicates it is transient
          return 5;
      // TODO: Pass in the permission level required and then do logic short-circuits if the permission level is found
      if (data.getUniversalPermissionLevel() == -1){
        User publicUser = UserSecurity.getPublicUser();
        if (publicUser != null)
          data.setUniversalPermissionLevel(publicUser.getPermissionLevel(data).level);
      }*/
      if(!data.getId().isPersisted())
        return 5;

        int permissionLevel = UserSecurity.getPermissionLevel(data);
        if (permissionLevel < level) {
          String message;
          if (level == BROWSE || level == LIMITED)
            message = "Access denied to " + data + " you do not have any permission";
          else if (level == READ)
            message = "Access denied to " + data + " you do not have read permission";
          else if (level == WRITE)
            message = "Writing to " + data + " is not permitted, you do not have write permission";
          else if (level == EXECUTE)
              message = "Executing a method on " + data + " is not permitted, you do not have execute permission";
          else if (level == APPEND)
              message = "Appending to " + data + " is not permitted, you do not have append permission";
          else if (level == ACCESS_PERMISSION_LEVEL)
              message = "Deleting " + data + " is not permitted, you do not have delete permission";
          else
            message = "Unknown security exception";

          throw ScriptRuntime.constructError("AccessError",message);
        }
        return permissionLevel;
    }




  public Object get(int index) {
    return get(index,this);
  }
  @Override
  public Object get(int index, Scriptable start) {
      if (id != null && id.source != null) {
        if(securityEnabled.get() != null){
          checkSecurity(this, PermissionLevel.READ_LEVEL.level);
        }

      }
      Object value = super.get(index,this);
        try {
      while (value instanceof TargetRetriever)
        value = ((TargetRetriever)value).getTarget();
    } catch (ObjectNotFoundException e) {
      value = null;
    }
        if (value instanceof Date) // TODO: Do we need to find a way to make this return the same date each time, I don't know if it is even possible with objects being collected
          return ScriptRuntime.newObject(PersevereContextFactory.getContext(), GlobalData.getGlobalScope(), "Date", new Object[] {((Date)value).getTime()});
        return value;
    }


     public Date getLastModified() {
       //TODO: Need to implement this with the new history system
    return lastModified;
  }
    Date lastModified = new Date();
    public static final int ENTRY_SET_INCLUDE_DONT_ENUM = 1;
    public static final int ENTRY_SET_INCLUDE_GETTER_SETTER_FUNCTIONS = 2;
    public static final int ENTRY_SET_USE_CORE_VALUES = 4;
    class GetterSetterCombo {
      Function getter;
      Function setter;
    }
    Object getPreGetterSetterValue(String name, int index){
      Object getter = getGetterOrSetter(name, index, false);
      Object setter = getGetterOrSetter(name, index, false);
      if (getter instanceof Function || setter instanceof Function){
        GetterSetterCombo value = new GetterSetterCombo();
        value.getter = (Function) (getter instanceof Function ? getter : null);
        value.setter = (Function) (setter instanceof Function ? setter : null);
      }
      if(name == null)
        return get(index, this);
      return get(name, this);
    }
    static private class EntrySet extends AbstractSet<Map.Entry<String,Object>>{
      Object[] ids;
      Scriptable object;
      boolean useCoreValues;
      boolean includeGettersSetters;
      EntrySet(Object[] ids, Scriptable object, boolean useCoreValues, boolean includeGettersAndSetters){
        this.ids = ids;
        this.object = object;
        this.useCoreValues = useCoreValues && object instanceof PersistableObject;
        this.includeGettersSetters = includeGettersAndSetters && object instanceof PersistableObject;
      }
    @Override
    public Iterator iterator() {
      return new Iterator(){
        int i = 0;
        Object nextValue = getNextValue();
        String nextKey;
          public boolean hasNext(){
            return nextValue != Scriptable.NOT_FOUND;
          }
          Object getNextValue(){
            do{
              nextValue = ids.length > i ? (useCoreValues ? ((PersistableObject)object).getCoreValue(ids[i].toString()) :
                includeGettersSetters ? ((PersistableObject)object).getPreGetterSetterValue(ids[i] instanceof String ? (String) ids[i] : null, ids[i] instanceof Number ? ((Number)ids[i]).intValue() : 0) :
                object.get(ids[i].toString(), object)) : Scriptable.NOT_FOUND;
              nextKey = ids.length > i ? ids[i].toString() : null;
              i++;
            }
            while(ids.length > i && nextValue == Scriptable.NOT_FOUND);
            return nextValue;
          }
          public Map.Entry<String,Object> next(){
            final String name = nextKey;
            final Object value = nextValue;
            getNextValue();
            return new Map.Entry<String,Object>(){
              public String getKey(){
                return name;
              }
              public Object getValue(){
                return value;
              }
              public Object setValue(Object obj){
                throw new UnsupportedOperationException();
              }

            };
          }
          public void remove(){
            throw new UnsupportedOperationException();
          }

      };
    }

    @Override
    public int size() {
      return ids.length;
    }

    }
    static public Set<Map.Entry<String, Object>> entrySet(ScriptableObject target, int options){
      PersevereContextFactory.getContext();
      return new EntrySet(
          ((ENTRY_SET_INCLUDE_DONT_ENUM & options) == ENTRY_SET_INCLUDE_DONT_ENUM) ? target.getAllIds() : target.getIds(),
          target,
          ((ENTRY_SET_USE_CORE_VALUES & options) == ENTRY_SET_USE_CORE_VALUES),
          ((ENTRY_SET_INCLUDE_GETTER_SETTER_FUNCTIONS & options) == ENTRY_SET_INCLUDE_GETTER_SETTER_FUNCTIONS));
    }
    public Set<Map.Entry<String, Object>> entrySet(int options){
      if (id != null && id.source != null) {
        if(securityEnabled.get() != null){
          checkSecurity(this, PermissionLevel.READ_LEVEL.level);
        }
      }
      return new EntrySet(
          ((ENTRY_SET_INCLUDE_DONT_ENUM & options) == ENTRY_SET_INCLUDE_DONT_ENUM) ? getAllIds() : getIds(),
          this,
          ((ENTRY_SET_USE_CORE_VALUES & options) == ENTRY_SET_USE_CORE_VALUES),
          ((ENTRY_SET_INCLUDE_GETTER_SETTER_FUNCTIONS & options) == ENTRY_SET_INCLUDE_GETTER_SETTER_FUNCTIONS));
    }
  public Set<String> keySet(boolean includeDontEnum) {
    Object[] ids = includeDontEnum ? getAllIds() : getIds();
    Set<String> keySet = new HashSet();
    for (Object key : ids)
        keySet.add(key.toString());
    return keySet;
  }
  public void subscribe() {
      if (id != null && id.source != null) {
        if(securityEnabled.get() != null){
          checkSecurity(this, PermissionLevel.READ_LEVEL.level);
        }
          Map<ObjectId, Set<String>> readSet = readSets.get();
          if (readSet != null) {
            readSet.put(id, FullSet.instance);
          }
      }
  }
    @Override
  public Object[] getIds() {
/*      if (id != null && id.source != null) {
        recordObjectRead();
      }*/
    return super.getIds();
  }
  @Override
  public String toString() {
      return getId().isPersisted() ? getId().toString() : super.toString();
    }
/*    private long getLengthProperty() {
        // These will both give numeric lengths within Uint32 range.
      if (id != null && id.source != null && id.isPersisted())
        recordObjectRead();*
        if (this instanceof PersistentList)
            return ((PersistentList)this).getLength();
        Object length = ScriptRuntime.getObjectProp(this, "length", PersevereContextFactory.getContext());
        if (length instanceof Number)
          return ScriptRuntime.toUint32(length);
        return 0;
    }*/
  public void delete() {
    if(PersistableObject.securityEnabled.get() != null){
      PersistableObject.checkSecurity(this, PermissionLevel.FULL_LEVEL.level);
    }
    // TODO: The reference cleanup is not transactionally isolated
    if(id.source instanceof ReferenceAwareDataSource){
      List<ObjectId> referrers = ((ReferenceAwareDataSource)id.source).getReferrers(id.subObjectId);
      for (ObjectId objRef: referrers) {
        Persistable obj = objRef.getTarget();
        if (obj instanceof List)
          ((List)obj).remove(this);
        else {
          for (Map.Entry<String,Object> entry : obj.entrySet(1)) {
            if (entry.getValue() == this) {
              ((Persistable)entry.getValue()).delete(entry.getKey());
            }
          }
        }
      }
    }
    asTransactionValue(null, this, Scriptable.NOT_FOUND);
  }
  public Persistable getParent() {
    if(parent == this){
      throw new RuntimeException("circular loop in parents");
    }
    return (Persistable) (parent instanceof Persistable ? parent :
          parent instanceof Query ? parent = ((Query)parent).getCachedTarget() :
          parent instanceof ObjectId ? parent = ((ObjectId) parent).getTarget() :
          id != null && id.source != null ?
                parent = ((Query) ObjectId.idForObject(id.source, "")).getCachedTarget() :
            null);

  }

    public static class FullSet implements Set<String> {
      public final static FullSet instance = new FullSet();
    public boolean add(String e) {
      return true;
    }

    public boolean addAll(Collection c) {
      return true;
    }

    public void clear() {
    }

    public boolean contains(Object o) {
      return true;
    }

    public boolean containsAll(Collection c) {
      return true;
    }

    public boolean isEmpty() {
      return false;
    }

    public Iterator iterator() {
      return null;
    }

    public boolean remove(Object o) {
      return false;
    }

    public boolean removeAll(Collection c) {
      return false;
    }

    public boolean retainAll(Collection c) {
      return false;
    }

    public int size() {
      return 0;
    }

    public Object[] toArray() {
      return null;
    }

    public Object[] toArray(Object[] a) {
      return null;
    }

    }
  public void onCreation() {
    // do nothing
  }
  /*public int getUniversalPermissionLevel() {
    return universalPermissionLevel;
  }
  public void setUniversalPermissionLevel(int universalPermissionLevel) {
    this.universalPermissionLevel = universalPermissionLevel;
  }*/
  Version version;
  public Version getVersion() {
    return version;
  }

}
TOP

Related Classes of org.persvr.data.PersistableObject$PersistentInitializer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.