Package org.persvr.data

Source Code of org.persvr.data.PersistableArray$Itr

package org.persvr.data;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.BaseFunction;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.IdFunctionObject;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.NativeIterator;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.persvr.data.PersistableObject.FullSet;
import org.persvr.datasource.ChangeableData;
import org.persvr.datasource.ReferenceAwareDataSource;
import org.persvr.javascript.PersevereContextFactory;
import org.persvr.javascript.PersevereNativeFunction;
import org.persvr.security.PermissionLevel;
import org.persvr.security.SystemPermission;
import org.persvr.security.UserSecurity;
import org.persvr.util.CompareValues;

public class PersistableArray extends NativeArray implements List,PersistableList,ObservablePersistable, PropertyChangeListener {
  public static class ElementsLoader {
    int initializedLength = Integer.MAX_VALUE;
    Iterator iterator;
    boolean initialized;
  }
  ElementsLoader elementsLoader;
    public PersistableArray(long lengthArg)
    {
        super(0);
        super.put("length", this, lengthArg);
    }

    public PersistableArray(Object[] array){
      super(0);
    // this puts the array in sparse mode, so that elements
    // are looked up within all internal methods
    super.put("length", this, array.length);
    int i = 0;
      for(Object object : array){
        super.put(i++, this, object);
      }
    }
  public void initSourceCollection(Collection sourceCollection){
    ElementsLoader elementsLoader = new ElementsLoader();
    elementsLoader.iterator = sourceCollection.iterator();
    elementsLoader.initializedLength = 0;
    this.elementsLoader = elementsLoader;
    superPutLength(sourceCollection.size());
    elementsLoader.initialized = true;
  }
  protected void fullyFetch(){
    if (elementsLoader != null && elementsLoader.iterator != null)
      fetchNextPage(Integer.MAX_VALUE);
  }
  public static long maxIterations = 10000;
 
  static class FilterFunction extends BaseFunction{
    Function defaultFunction;
   
    public FilterFunction(Function defaultFunction) {
      super();
      this.defaultFunction = defaultFunction;
    }
    //TODO: Do a custom map function as well
    @Override
    public Object call(final Context cx, final Scriptable scope, final Scriptable thisObj, Object[] args) {
      // try to do a pass-through to the data source
      if (thisObj instanceof Persistable && ((Persistable) thisObj).getId() instanceof Query){
        try {
          return Query.parseQuery((Query) ((Persistable) thisObj).getId(), scope, ((Function)args[0]), true, false).getTarget();
        } catch (QueryCantBeHandled e) {
          // pass through failed, we will go to the default handler
          LogFactory.getLog(PersistableArray.class).debug("query can't be handled at the database level (will be filtered in JavaScript) " + e);
        }
      }
      if (thisObj instanceof QueryArray){
            Object callbackArg = args.length > 0 ? args[0] : Undefined.instance;
            if (callbackArg == null || !(callbackArg instanceof Function)) {
                throw ScriptRuntime.notFunctionError(
                         ScriptRuntime.toString(callbackArg));
            }
            final Function f = (Function) callbackArg;

        return new QueryArray(null){
          long sizeEstimate = ((QueryArray)thisObj).estimatedSize(0);
          int lastSizeEstimateIndex = -1;
          @Override
          public Iterator iterator() {
            final Iterator sourceIterator = ((List)thisObj).iterator();
           
            return new Iterator(){
              int i = -1;
              Object next;
              private void getNext(){
                do{
                  next = Scriptable.NOT_FOUND;
                  if(!sourceIterator.hasNext())
                    return;
                  i++;
                  if(i % 100 == 0){
                    if(i > maxIterations && PersistableObject.isSecurityEnabled() && !UserSecurity.hasPermission(SystemPermission.runLongQueries)){
                      throw ScriptRuntime.constructError("AccessError", "Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed.");
                    }
                  }
                  next = sourceIterator.next();
                  if(ScriptRuntime.toBoolean(f.call(cx, scope, thisObj, new Object[]{next, i, thisObj})))
                    return;
                  if(i > lastSizeEstimateIndex){
                    lastSizeEstimateIndex = i;
                    sizeEstimate--;
                  }
                }while(true);
              }
              public boolean hasNext() {
                if(next == null)
                  getNext();
                return next != Scriptable.NOT_FOUND;
               
              }

              public Object next() {
                if(next == null)
                  getNext();
                if(next == Scriptable.NOT_FOUND)
                        throw new JavaScriptException(
                                NativeIterator.getStopIterationObject(scope), null, 0);

                Object value = next;
                next = null;
                return value;
              }

              public void remove() {
                throw new UnsupportedOperationException("Not implemented yet");
              }
             
            };
          }

          @Override
          public int size() {
            int size = 0;
            for(Object val : this){
              size++;
            }
            return size;
          }
          @Override
          public long estimatedSize(long exactWithin) {
            if(exactWithin > lastSizeEstimateIndex){
              Iterator iterator = iterator();
              for(long i = 0; i < exactWithin && iterator.hasNext(); i++){
                iterator.next();
              }
            }
            return sizeEstimate;
          }
         
        };
      }
      return defaultFunction.call(cx, scope, thisObj, args);
    }

  }
 
  static class FastSortFunction extends BaseFunction{
    Function defaultFunction;
   
    public FastSortFunction(Function defaultFunction) {
      super();
      this.defaultFunction = defaultFunction;
    }
    @Override
    public Object call(final Context cx, final Scriptable scope, Scriptable thisObj, final Object[] args) {
      // try to do a pass-through to the data source
      if (thisObj instanceof Persistable){
        final boolean ascending = (Boolean)args[1];
        try {
          // first we try to do it at the data source level
          ObjectId id = ((Persistable) thisObj).getId();
          if(args.length < 3) {
            // TODO: once the data sources support multiple sort parameters, this should be removed
            if(id instanceof Query)
              return Query.parseQuery((Query) id, scope, ((Function)args[0]), false, ascending).getTarget();
          }
        } catch (QueryCantBeHandled e) {
          // pass through failed, we will go to the default handler
          LogFactory.getLog(PersistableArray.class).debug("query can't be handled at the database level (will be filtered in JavaScript) " + e);
        } catch (NullPointerException e){
          throw new RuntimeException("Sorting query can't be parsed", e);
        }
        List listToBeSorted = Arrays.asList(((List)thisObj).toArray());
        Collections.sort(listToBeSorted, new Comparator(){
          int i;
          public int compare(Object o1, Object o2) {
            int sortIndex = 0;
            i++;
            if(i % 100 == 0){
              if(i > maxIterations && PersistableObject.isSecurityEnabled() && !UserSecurity.hasPermission(SystemPermission.runLongQueries)){
                throw ScriptRuntime.constructError("AccessError", "Query has taken too much computation, and the user is not allowed to execute resource-intense queries");
              }
            }
            if (o1 instanceof Scriptable && o2 instanceof Scriptable){
              while(sortIndex < args.length){
                Function func = ((Function)args[sortIndex]);
                boolean ascending = (Boolean)args[sortIndex + 1];
                Object v1;
                try{
                  v1 = func.call(cx, scope, (Scriptable) o1, new Object[]{o1});
                }catch(EcmaError e){
                  v1 = Undefined.instance;
                }
                Object v2;
                try{
                  v2 = func.call(cx, scope, (Scriptable) o2, new Object[]{o2});
                }catch(EcmaError e){
                  v2 = Undefined.instance;
                }
                int comparison = CompareValues.instance.compare(v1, v2);
                if (comparison != 0)
                  return (ascending ? 1 : -1) * comparison;
                sortIndex += 2;
              }
            }
            return 0;
          }
         
        });
        return new QueryArray(listToBeSorted);
      }
      return defaultFunction.call(cx, scope, thisObj, args);
    }

  }

  static{
    ScriptableObject arrayPrototype = (ScriptableObject) ScriptableObject.getClassPrototype(GlobalData.getGlobalScope(),"Array");
    Function function = (Function) arrayPrototype.get("filter", arrayPrototype);
    arrayPrototype.put("filter", arrayPrototype,new FilterFunction((Function) arrayPrototype.get("filter",arrayPrototype)));
    final Function fastSortFunction = new FastSortFunction((Function) arrayPrototype.get("sort",arrayPrototype));
    arrayPrototype.put("fastSort", arrayPrototype,fastSortFunction);
    arrayPrototype.setAttributes("fastSort", ScriptableObject.DONTENUM);
    arrayPrototype.put("distinct", arrayPrototype, new PersevereNativeFunction(){

      @Override
      public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        // find all the distinct entries, easily done by adding it to a set
        Set distinctItems = new LinkedHashSet();
      
        if(((List)thisObj).size() > maxIterations && PersistableObject.isSecurityEnabled() && !UserSecurity.hasPermission(SystemPermission.runLongQueries)){
          throw ScriptRuntime.constructError("AccessError", "Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed.");
        }
        distinctItems.addAll((List) thisObj);
        // TODO: implement maxIterations check
        distinctItems.remove(Undefined.instance); // we don't want this in the list
        PersistableArray results = new PersistableArray(distinctItems.toArray());
        ScriptRuntime.setObjectProtoAndParent(results, GlobalData.getGlobalScope());
        return results;
      }
     
    });
    arrayPrototype.setAttributes("distinct", ScriptableObject.DONTENUM);
    arrayPrototype.put("contains", arrayPrototype, new PersevereNativeFunction(){

      @Override
      public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        // see if any item matches any of the arguments
        int i = 0;
        for(Object value : args){
          i++;
          if(i % 100 == 0){
            if(i > maxIterations && PersistableObject.isSecurityEnabled() && !UserSecurity.hasPermission(SystemPermission.runLongQueries)){
              throw ScriptRuntime.constructError("AccessError", "Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed.");
            }
          }
          if(value instanceof Function){
            Function func = (Function)value;
            for(Object obj : (List) thisObj){
              if(Boolean.TRUE.equals(func.call(cx, scope, thisObj, new Object[]{obj})))
                return true;
            }
          }
          if(value == null){
            // fast comparison for nulls
            for(Object obj : (List) thisObj){
              if(obj == null)
                return true;
            }
          }
          else if(value instanceof Number){
            // use the script runtime for numbers
            for(Object obj : (List) thisObj){
              if(ScriptRuntime.shallowEq(value, obj)){
                return true;
              }
            }
          }
          else {
            for(Object obj : (List) thisObj){
              if(value.equals(obj)){
                return true;
              }
            }
          }
        }
        return false;
      }
     
    });
    final Function identityFunction = new PersevereNativeFunction(){
      public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        return args[0];
      }
    };
    arrayPrototype.setAttributes("contains", ScriptableObject.DONTENUM);
    arrayPrototype.put("sum", arrayPrototype, new PersevereNativeFunction(){

      @Override
      public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        Function func = args.length == 0 ? identityFunction : (Function)args[0];
        double sum = 0;
        int i = 0;
        for(Object obj : (List) thisObj){
          i++;
          if(i % 100 == 0){
            if(i > maxIterations && PersistableObject.isSecurityEnabled() && !UserSecurity.hasPermission(SystemPermission.runLongQueries)){
              throw ScriptRuntime.constructError("AccessError", "Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed.");
            }
          }
          Object result = func.call(cx, scope, thisObj, new Object[]{obj});
          if (result instanceof Number)
            sum += ((Number)result).doubleValue();
        }
        return sum;
      }
     
    });
    arrayPrototype.setAttributes("sum", ScriptableObject.DONTENUM);
    arrayPrototype.put("max", arrayPrototype, new PersevereNativeFunction(){

      @Override
      public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        Function func = args.length == 0 ? identityFunction : (Function)args[0];
        double max = 0;
        boolean first = true;
        int i = 0;
        for(Object obj : (List) thisObj){
          i++;
          if(i % 100 == 0){
            if(i > maxIterations && PersistableObject.isSecurityEnabled() && !UserSecurity.hasPermission(SystemPermission.runLongQueries)){
              throw ScriptRuntime.constructError("AccessError", "Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed.");
            }
          }
          Object result = func.call(cx, scope, thisObj, new Object[]{obj});
          if (result instanceof Number){
            if(first){
              first = false;
              max = ((Number)result).doubleValue();
            }
            else{
              max = Math.max(max, ((Number)result).doubleValue());
            }
          }
           
        }
        return max;
      }
     
    });
    arrayPrototype.setAttributes("max", ScriptableObject.DONTENUM);
    arrayPrototype.put("min", arrayPrototype, new PersevereNativeFunction(){

      @Override
      public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        Function func = args.length == 0 ? identityFunction : (Function)args[0];
        double min = 0;
        boolean first = true;
        int i = 0;
        for(Object obj : (List) thisObj){
          i++;
          if(i % 100 == 0){
            if(i > maxIterations && PersistableObject.isSecurityEnabled() && !UserSecurity.hasPermission(SystemPermission.runLongQueries)){
              throw ScriptRuntime.constructError("AccessError", "Query has taken too much computation, and the user is not allowed to execute resource-intense queries. Increase maxIterations in your config file to allow longer running non-indexed queries to be processed.");
            }
          }

          Object result = func.call(cx, scope, thisObj, new Object[]{obj});
          if (result instanceof Number){
            if(first){
              first = false;
              min = ((Number)result).doubleValue();
            }
            else{
              min = Math.min(min, ((Number)result).doubleValue());
            }
          }
           
        }
        return min;
      }
     
    });
    arrayPrototype.setAttributes("min", ScriptableObject.DONTENUM);
    //TODO: finish the group method
    /*arrayPrototype.put("group", arrayPrototype, new PersevereNativeFunction(){

      @Override
      public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
        List sorted = (List) fastSortFunction.call(cx, scope, thisObj, args);
        List nextGroup, grouped = Persevere.newArray();
        
        for(Object item : sorted){
          if(!match){
            nextGroup = Persevere.newArray();
            grouped.add(nextGroup);
          }
          nextGroup.add(item);
        }
        return grouped;
      }
     
    });
    arrayPrototype.setAttributes("group", ScriptableObject.DONTENUM);*/
  }
  protected synchronized void fetchNextPage(int limit){
    //TODO: may be better to synchronize on the iterator itself
    if (elementsLoader != null && elementsLoader.iterator != null) {
      Iterator iterator = elementsLoader.iterator;
      while(iterator.hasNext()){
        super.put(elementsLoader.initializedLength, this, iterator.next());
        elementsLoader.initializedLength++;
        if (elementsLoader.initializedLength >= limit){
          return;
        }
      }
    }
    elementsLoader.iterator = null;
    elementsLoader.initializedLength = Integer.MAX_VALUE;
  }
  public Object remove(int index) {
    fullyFetch();
    Object result = get(index);
    ((Function)get("splice")).call(PersevereContextFactory.getContext(),GlobalData.getGlobalScope(),this,new Object[]{index,1});
    return result;
  }
  void noCheckSet(int index,Object value) {
    super.put(index,this,value);
  }
    public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
            Scriptable thisObj, Object[] args)
    {
      fullyFetch();
      return super.execIdCall(f, cx, scope, thisObj, args);
    }
  public List subList(int fromIndex, int toIndex) {
    if(fromIndex == 0 && toIndex == size())
      return this;
    return (List) ((Function)get("slice")).call(PersevereContextFactory.getContext(), GlobalData.getGlobalScope(), this, new Object[]{fromIndex,toIndex});
  }
  public Object get(int index) {
    return get(index,this);
 
  public static int fetchSize = 20;
  public boolean has(int index, Scriptable start) {
      if (id != null && id.source != null) {
        if (elementsLoader != null && index >= elementsLoader.initializedLength)
          fetchNextPage(index + fetchSize);
        if (transactionalVersion != null) {
          PersistableArray currentVersion = (PersistableArray) transactionalVersion.getTarget();
          if(currentVersion != this && currentVersion != null)
            return currentVersion.has(index, start);
        }
      }
      return super.has(index, start);
  }
  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)) {
          PersistableObject.mapPersistent(id);
        }
      }
    }
  }

  TransactionValue transactionalVersion;
  @Override
  public Object get(int index, Scriptable start) {   
      if (id != null && id.source != null) {
        updateObject();
        if (elementsLoader != null && index >= elementsLoader.initializedLength)
          fetchNextPage(index + fetchSize);
       
        if(PersistableObject.securityEnabled.get() != null){
          PersistableObject.checkSecurity(this, PermissionLevel.READ_LEVEL.level);
        }
        if (transactionalVersion != null) {
          PersistableArray currentVersion = (PersistableArray) transactionalVersion.getTarget();
          if(currentVersion != this && currentVersion != null)
            return currentVersion.get(index, start);
        }
          Object value = super.get(index,start);
            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;
      }
      return super.get(index,start);
    }
 
  @Override
  public long getLength() {
    if (id != null && id.source != null){
      updateObject();
      if(transactionalVersion != null) {
          PersistableArray currentVersion = (PersistableArray) transactionalVersion.getTarget();
          if(currentVersion != this && currentVersion != null)
            return currentVersion.getLength();
      }
    }

        return super.getLength();
  }
  void superPutLength(long length){
    super.put("length", this, length);
  }
  @Override
    protected Object getInstanceIdValue(int id)
    {
        if (elementsLoader != null && elementsLoader.iterator != null && id == 1) {
          getLength();
        }
        return super.getInstanceIdValue(id);
    }
  public void propertyChange(PropertyChangeEvent evt) {
    throw new UnsupportedOperationException("Not implemented yet");
  }
  public Object noCheckGet(String key) {
    return super.get(key,this);
  }
  public Object noCheckGet(int index) {
      if (id != null && id.source != null) {
        if (elementsLoader != null && index >= elementsLoader.initializedLength)
          fetchNextPage(index + fetchSize);
       
      }
      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 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 && id.subObjectId != null){
      if(id.subObjectId.length() == 0){
        ObjectId.idForObject(DataSourceManager.getMetaClassSource(), id.source.getId()).getTarget().delete();
        return;
      }
      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());
            }
          }
        }
      }
    }

    new TransactionValue(this,null, Scriptable.NOT_FOUND);
  }
  public void subscribe() {
      Map<ObjectId, Set<String>> readSet = PersistableObject.readSets.get();
      if (readSet != null) {
        readSet.put(id, FullSet.instance);
      }
  }

  public Object get(String key, Scriptable start) {
    if("length".equals(key)){
        if (transactionalVersion != null) {
          PersistableArray currentVersion = (PersistableArray) transactionalVersion.getTarget();
          if(currentVersion != this && currentVersion != null)
            return currentVersion.get(key);
        }
    }
    return super.get(key, start);
  }
  public Object get(String key) {
    if("length".equals(key)){
        if (transactionalVersion != null) {
          PersistableArray currentVersion = (PersistableArray) transactionalVersion.getTarget();
          if(currentVersion != this && currentVersion != null)
            return currentVersion.get(key);
        }
    }
      Object value = ScriptableObject.getProperty(this,key);
        while (value instanceof TargetRetriever)
          value = ((TargetRetriever)value).getTarget();
        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 int getAccessLevel() {
    return PersistableObject.checkSecurity(this, -1);
  }

  public PersistableList<Persistable> getHistory() {
    throw new UnsupportedOperationException("Not implemented yet");
  }
  public ObjectId getId() {
    if (id == null)
      return (id = new NewObjectId(this));
    return id;
  }
  public Persistable getParent() {
    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 ?
                (this == (parent = ((Query) ObjectId.idForObject(id.source, "")).getCachedTarget())) ? (parent = null) : parent :
            null);
  }
  public Persistable getSchema() {
    return schema;
  }
  public void onCreation() {
  }
  public Object set(String name, Object value) {
    put(name, this,value);
    return value;
  }
  int universalPermissionLevel;
  ObjectId id;
  Object parent;
  Persistable schema;
 
  static void commitIfImmediate() {
    Transaction.currentTransaction().commitIfImmediate();
  }
  static class TransactionValueArray extends TransactionValue {
    public TransactionValueArray(Persistable target, String property, Object defaultValue) {
      super(target, property, defaultValue);
    }
  }
  PersistableArray getOrCreateModTarget() {
    if (id !=null && id.source != null && !(id instanceof Query)) {
      if(transactionalVersion instanceof TransactionValueArray)
        return this;
      fullyFetch();
      if (transactionalVersion == null)
        transactionalVersion = new TransactionValue(this,null,this);
      PersistableArray target = (PersistableArray) transactionalVersion.getTarget();
      if (target == this) {
        try {
          target = getClass().getConstructor(Object[].class).newInstance(new Object[]{toArray()});
        }catch(Exception e){
          throw new RuntimeException(e);
        }
          target.setPrototype(getPrototype());
          target.id = id;
          target.parent = parent;
          target.universalPermissionLevel = universalPermissionLevel;
          target.transactionalVersion = new TransactionValueArray(target,null,target);
          transactionalVersion.setValue(this, null, target);
      }
      return target;
    }
    return this;
  }
  public void put(int index, Scriptable start, Object value)
    {
    if (id !=null && id.source != null && !(id instanceof Query) && start == this) {
     
      PersistableObject.checkPut(this,index,value);
      Persistable parent = getParent();
      for(Map.Entry<String, Object> entry : getParent().entrySet(1)){
        if(entry.getValue() == this){
          Object props = parent.getSchema().get("properties");
          if(props instanceof Persistable){
            Object propDef = ((Persistable)props).get(entry.getKey());
            if(propDef instanceof Persistable){
              value = PersistableClass.enforceSchemaForProperty((Persistable) propDef, this, index,value, true, false, true);
            }
          }
        }
      }
      PersistableArray target = getOrCreateModTarget();
      target.superPut(index, target, value);
      commitIfImmediate();
      return;
      }
      super.put(index, start, value);
    }
  private void superPut(int index, Scriptable start, Object value){
    super.put(index, start, value);
  }
 
    @Override
  public void put(String name, Scriptable start, Object value) {
      if (id != null && id.source != null && !(id instanceof Query) && "length".equals(name)){
        if(PersistableObject.securityEnabled.get() != null){
          PersistableObject.checkSecurity(this, PermissionLevel.WRITE_LEVEL.level);
        }

        PersistableArray target = getOrCreateModTarget();
        target.superPut(name, target, value);
      }
      else
        super.put(name, start, value);
   
  }
  private void superPut(String name, Scriptable start, Object value){
    super.put(name, start, value);
  }
  public void delete(int index)
    {
    if (id != null && id.source != null && !(id instanceof Query)) {
        if(PersistableObject.securityEnabled.get() != null){
          PersistableObject.checkSecurity(this, PermissionLevel.WRITE_LEVEL.level);
        }
        if(elementsLoader == null || elementsLoader.initialized){
          getOrCreateModTarget().superDelete(index);
          commitIfImmediate();
        }
      }
        super.delete(index);
    }
    private void superDelete(int index){
      super.delete(index);
    }
  boolean isModifying = false;

  public Set<String> keySet(boolean includeDontEnum) { // an array should not include the integers
    Object[] ids = getIds();
    Set<String> keySet = new HashSet();
    for (Object id : ids)
      if (id instanceof String)
        keySet.add((String) id);
    return keySet;
  }
  public Set<Map.Entry<String, Object>> entrySet(int options){
    return new HashSet();
  }
  public boolean add(Object value) {   
    put(size(), this, value);
/*    if (isPersistingList) {
      DataObject historyEntry = newHistoryEntry();
      if (historyEntry != null)
        historyEntry.set(GlobalData.BASIS_FIELD,convertToIdIfNeeded(Templates.findTemplate("historyAppend")));
    }*/
       return true;
    }

    public Object set(int index, Object element) {
      put(index,this,element);
      return element;
    }
   /* @Override
  public void put(int index, Scriptable start, Object value)
    {
      if (isPersistingList && !isModifying) {
        int size = size();
          if (index == size)
            checkAppendSecurity();
          else
            checkWriteSecurity(this);
           try {
          Identification id = getId();
          if (id.source != null) {
            if (id.source instanceof IndexedListDataSource && index != size)
              ((IndexedListDataSource) id.source).recordListReplace(id.objectId, index, convertToIdIfNeeded(value));
            else {
              if (index != size)
                delete(index);
              id.source.recordListAdd(id.objectId, convertToIdIfNeeded(value));
            }
          }
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
      }
       super.put(index, start, value);
    }*/
    public void add(int index, Object element) {
        /*   if (data == null)
               data = new TransientDataObjectList();*/
    fullyFetch();
      PersistableObject.enforceObjectChange(this,0, element, true);
      Object oldValue = super.get(index,this);
      if (id != null && id.source != null && !(id instanceof Query)) {
        if(PersistableObject.securityEnabled.get() != null){
          PersistableObject.checkSecurity(this, PermissionLevel.WRITE_LEVEL.level);
        }

         }
      boolean middle = index != ((PersistableArray)this).size();
      put(index,this,element);
      if (middle)
        add(index+1,oldValue);
   }

    public boolean remove(Object o) {
    fullyFetch();

      int index = indexOf(o);
      if (index == -1)
        return false;
      //TODO: Must set this directly, this is terribly inefficient
    remove(index);
      return true;
    }

    /*protected boolean addFromSourceIterator() {
      if (iterator.hasNext()) {
        super.put((int) ((NativeArray)this).getLength(), this, iterator.next());
        return true;
      }
      return false;
    }*/
/*  @Override
  public Object get(int index, Scriptable start) {
   
    Object value;
    do {
      value = super.get(index,this);
    }
    while (value == Scriptable.NOT_FOUND && addFromSourceIterator());
        return value;
    }
    */
    
    
    public int size() {
      return (int) getLength();
  }
  @Override
  public String getClassName() {
    return "Array";
  }
/*  public static PersistentList filter(List superList, Stipulation stipulation) {
    QueryId filterObjectId = QueryId.idForObject(((PersistentList)superList).id.source, ((PersistentList)superList).id.subObjectId, stipulation);
    return (PersistentList) filterObjectId.getTarget();
  }
  Iterator iterator;
  public Stipulation getStipulation() {
    if (id instanceof QueryId)
      return ((QueryId)id).stipulation;
    else
      return null;
  }*/
  /* From AbstractCollection */
    /**
     * {@inheritDoc}
     *
     * <p>This implementation returns <tt>size() == 0</tt>.
     */
    public boolean isEmpty() {
  return size() == 0;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This implementation iterates over the elements in the collection,
     * checking each element in turn for equality with the specified element.
     *
     * @throws ClassCastException   {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public boolean contains(Object o) {
    fullyFetch();
  Iterator e = iterator();
  if (o==null) {
      while (e.hasNext())
    if (e.next()==null)
        return true;
  } else {
      while (e.hasNext())
    if (o.equals(e.next()))
        return true;
  }
  return false;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This implementation returns an array containing all the elements
     * returned by this collection's iterator, in the same order, stored in
     * consecutive elements of the array, starting with index {@code 0}.
     * The length of the returned array is equal to the number of elements
     * returned by the iterator, even if the size of this collection changes
     * during iteration, as might happen if the collection permits
     * concurrent modification during iteration.  The {@code size} method is
     * called only as an optimization hint; the correct result is returned
     * even if the iterator returns a different number of elements.
     *
     * <p>This method is equivalent to:
     *
     *  <pre> {@code
     * List list = new ArrayList(size());
     * for (E e : this)
     *     list.add(e);
     * return list.toArray();
     * }</pre>
     */
    public Object[] toArray() {
        // Estimate size of array; be prepared to see more or fewer elements
  Object[] r = new Object[size()];
        Iterator it = iterator();
  for (int i = 0; i < r.length; i++) {
      if (! it.hasNext())  {// fewer elements than expected
        return copyOf(r, i);
      }
      r[i] = it.next();
  }
  return it.hasNext() ? finishToArray(r, it) : r;
    }
    private static Object[] copyOf(Object[] array, int length) {
      Object[] newArray = new Object[length];
      System.arraycopy(array, 0, newArray, 0, Math.min(length, array.length));
      return newArray;
     
    }
    /**
     * {@inheritDoc}
     *
     * <p>This implementation returns an array containing all the elements
     * returned by this collection's iterator in the same order, stored in
     * consecutive elements of the array, starting with index {@code 0}.
     * If the number of elements returned by the iterator is too large to
     * fit into the specified array, then the elements are returned in a
     * newly allocated array with length equal to the number of elements
     * returned by the iterator, even if the size of this collection
     * changes during iteration, as might happen if the collection permits
     * concurrent modification during iteration.  The {@code size} method is
     * called only as an optimization hint; the correct result is returned
     * even if the iterator returns a different number of elements.
     *
     * <p>This method is equivalent to:
     *
     *  <pre> {@code
     * List list = new ArrayList(size());
     * for (E e : this)
     *     list.add(e);
     * return list.toArray(a);
     * }</pre>
     *
     * @throws ArrayStoreException  {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public Object[] toArray(Object[] a) {
    fullyFetch();

        // Estimate size of array; be prepared to see more or fewer elements
        int size = size();
        Object[] r = a.length >= size ? a :
                  (Object[])java.lang.reflect.Array
                  .newInstance(a.getClass().getComponentType(), size);
        Iterator it = iterator();

  for (int i = 0; i < r.length; i++) {
      if (! it.hasNext()) { // fewer elements than expected
    if (a != r)
        return copyOf(r, i);
    r[i] = null; // null-terminate
    return r;
      }
      r[i] = it.next();
  }
  return it.hasNext() ? finishToArray(r, it) : r;
    }

    /**
     * Reallocates the array being used within toArray when the iterator
     * returned more elements than expected, and finishes filling it from
     * the iterator.
     *
     * @param r the array, replete with previously stored elements
     * @param it the in-progress iterator over this collection
     * @return array containing the elements in the given array, plus any
     *         further elements returned by the iterator, trimmed to size
     */
    private static  Object[] finishToArray(Object[] r, Iterator<?> it) {
  int i = r.length;
        while (it.hasNext()) {
            int cap = r.length;
            if (i == cap) {
                int newCap = ((cap / 2) + 1) * 3;
                if (newCap <= cap) { // integer overflow
        if (cap == Integer.MAX_VALUE)
      throw new OutOfMemoryError
          ("Required array size too large");
        newCap = Integer.MAX_VALUE;
    }
    r = copyOf(r, newCap);
      }
      r[i++] = it.next();
        }
        // trim if overallocated
        return (i == r.length) ? r : copyOf(r, i);
    }

    // Bulk Operations

    /**
     * {@inheritDoc}
     *
     * <p>This implementation iterates over the specified collection,
     * checking each element returned by the iterator in turn to see
     * if it's contained in this collection.  If all elements are so
     * contained <tt>true</tt> is returned, otherwise <tt>false</tt>.
     *
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     * @see #contains(Object)
     */
    public boolean containsAll(Collection c) {
  Iterator<?> e = c.iterator();
  while (e.hasNext())
      if (!contains(e.next()))
    return false;
  return true;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This implementation iterates over the specified collection, and adds
     * each object returned by the iterator to this collection, in turn.
     *
     * <p>Note that this implementation will throw an
     * <tt>UnsupportedOperationException</tt> unless <tt>add</tt> is
     * overridden (assuming the specified collection is non-empty).
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     * @throws IllegalArgumentException      {@inheritDoc}
     * @throws IllegalStateException         {@inheritDoc}
     *
     * @see #add(Object)
     */
    public boolean addAll(Collection c) {
  boolean modified = false;
  Iterator e = c.iterator();
  while (e.hasNext()) {
      if (add(e.next()))
    modified = true;
  }
  return modified;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This implementation iterates over this collection, checking each
     * element returned by the iterator in turn to see if it's contained
     * in the specified collection.  If it's so contained, it's removed from
     * this collection with the iterator's <tt>remove</tt> method.
     *
     * <p>Note that this implementation will throw an
     * <tt>UnsupportedOperationException</tt> if the iterator returned by the
     * <tt>iterator</tt> method does not implement the <tt>remove</tt> method
     * and this collection contains one or more elements in common with the
     * specified collection.
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     *
     * @see #remove(Object)
     * @see #contains(Object)
     */
    public boolean removeAll(Collection c) {
  boolean modified = false;
  Iterator<?> e = iterator();
  while (e.hasNext()) {
      if (c.contains(e.next())) {
    e.remove();
    modified = true;
      }
  }
  return modified;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This implementation iterates over this collection, checking each
     * element returned by the iterator in turn to see if it's contained
     * in the specified collection.  If it's not so contained, it's removed
     * from this collection with the iterator's <tt>remove</tt> method.
     *
     * <p>Note that this implementation will throw an
     * <tt>UnsupportedOperationException</tt> if the iterator returned by the
     * <tt>iterator</tt> method does not implement the <tt>remove</tt> method
     * and this collection contains one or more elements not present in the
     * specified collection.
     *
     * @throws UnsupportedOperationException {@inheritDoc}
     * @throws ClassCastException            {@inheritDoc}
     * @throws NullPointerException          {@inheritDoc}
     *
     * @see #remove(Object)
     * @see #contains(Object)
     */
    public boolean retainAll(Collection c) {
  boolean modified = false;
  Iterator e = iterator();
  while (e.hasNext()) {
      if (!c.contains(e.next())) {
    e.remove();
    modified = true;
      }
  }
  return modified;
    }


    
     /* From AbstractList */





     // Search Operations

     /**
      * {@inheritDoc}
      *
      * <p>This implementation first gets a list iterator (with
      * {@code listIterator()}).  Then, it iterates over the list until the
      * specified element is found or the end of the list is reached.
      *
      * @throws ClassCastException   {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
      */
     public int indexOf(Object o) {
   ListIterator e = listIterator();
   if (o==null) {
       while (e.hasNext())
     if (e.next()==null)
         return e.previousIndex();
   } else {
       while (e.hasNext())
     if (o.equals(e.next()))
         return e.previousIndex();
   }
   return -1;
     }

     /**
      * {@inheritDoc}
      *
      * <p>This implementation first gets a list iterator that points to the end
      * of the list (with {@code listIterator(size())}).  Then, it iterates
      * backwards over the list until the specified element is found, or the
      * beginning of the list is reached.
      *
      * @throws ClassCastException   {@inheritDoc}
      * @throws NullPointerException {@inheritDoc}
      */
     public int lastIndexOf(Object o) {
   ListIterator e = listIterator(size());
   if (o==null) {
       while (e.hasPrevious())
     if (e.previous()==null)
         return e.nextIndex();
   } else {
       while (e.hasPrevious())
     if (o.equals(e.previous()))
         return e.nextIndex();
   }
   return -1;
     }


     // Bulk Operations

     /**
      * Removes all of the elements from this list (optional operation).
      * The list will be empty after this call returns.
      *
      * <p>This implementation calls {@code removeRange(0, size())}.
      *
      * <p>Note that this implementation throws an
      * {@code UnsupportedOperationException} unless {@code remove(int
      * index)} or {@code removeRange(int fromIndex, int toIndex)} is
      * overridden.
      *
      * @throws UnsupportedOperationException if the {@code clear} operation
      *         is not supported by this list
      */
     public void clear() {
       put("length",this,0);
         //removeRange(0, size());
     }

     /**
      * {@inheritDoc}
      *
      * <p>This implementation gets an iterator over the specified collection
      * and iterates over it, inserting the elements obtained from the
      * iterator into this list at the appropriate position, one at a time,
      * using {@code add(int, E)}.
      * Many implementations will override this method for efficiency.
      *
      * <p>Note that this implementation throws an
      * {@code UnsupportedOperationException} unless
      * {@link #add(int, Object) add(int, E)} is overridden.
      *
      * @throws UnsupportedOperationException {@inheritDoc}
      * @throws ClassCastException            {@inheritDoc}
      * @throws NullPointerException          {@inheritDoc}
      * @throws IllegalArgumentException      {@inheritDoc}
      * @throws IndexOutOfBoundsException     {@inheritDoc}
      */
     public boolean addAll(int index, Collection c) {
   boolean modified = false;
   Iterator e = c.iterator();
   while (e.hasNext()) {
       add(index++, e.next());
       modified = true;
   }
   return modified;
     }


     // Iterators

     /**
      * Returns an iterator over the elements in this list in proper sequence.
      *
      * <p>This implementation returns a straightforward implementation of the
      * iterator interface, relying on the backing list's {@code size()},
      * {@code get(int)}, and {@code remove(int)} methods.
      *
      * <p>Note that the iterator returned by this method will throw an
      * {@code UnsupportedOperationException} in response to its
      * {@code remove} method unless the list's {@code remove(int)} method is
      * overridden.
      *
      * <p>This implementation can be made to throw runtime exceptions in the
      * face of concurrent modification, as described in the specification
      * for the (protected) {@code modCount} field.
      *
      * @return an iterator over the elements in this list in proper sequence
      *
      * @see #modCount
      */
     public Iterator iterator() {
   return new Itr();
     }

     /**
      * {@inheritDoc}
      *
      * <p>This implementation returns {@code listIterator(0)}.
      *
      * @see #listIterator(int)
      */
     public ListIterator listIterator() {
   return listIterator(0);
     }

     /**
      * {@inheritDoc}
      *
      * <p>This implementation returns a straightforward implementation of the
      * {@code ListIterator} interface that extends the implementation of the
      * {@code Iterator} interface returned by the {@code iterator()} method.
      * The {@code ListIterator} implementation relies on the backing list's
      * {@code get(int)}, {@code set(int, E)}, {@code add(int, E)}
      * and {@code remove(int)} methods.
      *
      * <p>Note that the list iterator returned by this implementation will
      * throw an {@code UnsupportedOperationException} in response to its
      * {@code remove}, {@code set} and {@code add} methods unless the
      * list's {@code remove(int)}, {@code set(int, E)}, and
      * {@code add(int, E)} methods are overridden.
      *
      * <p>This implementation can be made to throw runtime exceptions in the
      * face of concurrent modification, as described in the specification for
      * the (protected) {@code modCount} field.
      *
      * @throws IndexOutOfBoundsException {@inheritDoc}
      *
      * @see #modCount
      */
     public ListIterator listIterator(final int index) {
   if (index<0 || index>size())
     throw new IndexOutOfBoundsException("Index: "+index);

   return new ListItr(index);
     }

     private class Itr implements Iterator {
   /**
    * Index of element to be returned by subsequent call to next.
    */
   int cursor = 0;

   /**
    * Index of element returned by most recent call to next or
    * previous.  Reset to -1 if this element is deleted by a call
    * to remove.
    */
   int lastRet = -1;

   /**
    * The modCount value that the iterator believes that the backing
    * List should have.  If this expectation is violated, the iterator
    * has detected concurrent modification.
    */
   int expectedModCount = modCount;

   public boolean hasNext() {
             return cursor != size();
   }

   public Object next() {
             checkForComodification();
       try {
     Object next = get(cursor);
     lastRet = cursor++;
     return next;
       } catch (IndexOutOfBoundsException e) {
     checkForComodification();
     throw new NoSuchElementException();
       }
   }

   public void remove() {
       if (lastRet == -1)
     throw new IllegalStateException();
             checkForComodification();

       try {
     PersistableArray.this.remove(lastRet);
     if (lastRet < cursor)
         cursor--;
     lastRet = -1;
     expectedModCount = modCount;
       } catch (IndexOutOfBoundsException e) {
     throw new ConcurrentModificationException();
       }
   }

   final void checkForComodification() {
       if (modCount != expectedModCount)
     throw new ConcurrentModificationException();
   }
     }

     private class ListItr extends Itr implements ListIterator {
   ListItr(int index) {
       cursor = index;
   }

   public boolean hasPrevious() {
       return cursor != 0;
   }

         public Object previous() {
             checkForComodification();
             try {
                 int i = cursor - 1;
                 Object previous = get(i);
                 lastRet = cursor = i;
                 return previous;
             } catch (IndexOutOfBoundsException e) {
                 checkForComodification();
                 throw new NoSuchElementException();
             }
         }

   public int nextIndex() {
       return cursor;
   }

   public int previousIndex() {
       return cursor-1;
   }

   public void set(Object e) {
       if (lastRet == -1)
     throw new IllegalStateException();
             checkForComodification();

       try {
     PersistableArray.this.set(lastRet, e);
     expectedModCount = modCount;
       } catch (IndexOutOfBoundsException ex) {
     throw new ConcurrentModificationException();
       }
   }

   public void add(Object e) {
             checkForComodification();

       try {
     PersistableArray.this.add(cursor++, e);
     lastRet = -1;
     expectedModCount = modCount;
       } catch (IndexOutOfBoundsException ex) {
     throw new ConcurrentModificationException();
       }
   }
     }


     /**
      * Removes from this list all of the elements whose index is between
      * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
      * Shifts any succeeding elements to the left (reduces their index).
      * This call shortens the ArrayList by {@code (toIndex - fromIndex)}
      * elements.  (If {@code toIndex==fromIndex}, this operation has no
      * effect.)
      *
      * <p>This method is called by the {@code clear} operation on this list
      * and its subLists.  Overriding this method to take advantage of
      * the internals of the list implementation can <i>substantially</i>
      * improve the performance of the {@code clear} operation on this list
      * and its subLists.
      *
      * <p>This implementation gets a list iterator positioned before
      * {@code fromIndex}, and repeatedly calls {@code ListIterator.next}
      * followed by {@code ListIterator.remove} until the entire range has
      * been removed.  <b>Note: if {@code ListIterator.remove} requires linear
      * time, this implementation requires quadratic time.</b>
      *
      * @param fromIndex index of first element to be removed
      * @param toIndex index after last element to be removed
      */
     protected void removeRange(int fromIndex, int toIndex) {
         ListIterator it = listIterator(fromIndex);
         for (int i=0, n=toIndex-fromIndex; i<n; i++) {
             it.next();
             it.remove();
         }
     }

     /**
      * The number of times this list has been <i>structurally modified</i>.
      * Structural modifications are those that change the size of the
      * list, or otherwise perturb it in such a fashion that iterations in
      * progress may yield incorrect results.
      *
      * <p>This field is used by the iterator and list iterator implementation
      * returned by the {@code iterator} and {@code listIterator} methods.
      * If the value of this field changes unexpectedly, the iterator (or list
      * iterator) will throw a {@code ConcurrentModificationException} in
      * response to the {@code next}, {@code remove}, {@code previous},
      * {@code set} or {@code add} operations.  This provides
      * <i>fail-fast</i> behavior, rather than non-deterministic behavior in
      * the face of concurrent modification during iteration.
      *
      * <p><b>Use of this field by subclasses is optional.</b> If a subclass
      * wishes to provide fail-fast iterators (and list iterators), then it
      * merely has to increment this field in its {@code add(int, E)} and
      * {@code remove(int)} methods (and any other methods that it overrides
      * that result in structural modifications to the list).  A single call to
      * {@code add(int, E)} or {@code remove(int)} must add no more than
      * one to this field, or the iterators (and list iterators) will throw
      * bogus {@code ConcurrentModificationExceptions}.  If an implementation
      * does not wish to provide fail-fast iterators, this field may be
      * ignored.
      */
     protected transient int modCount = 0;
  public int getUniversalPermissionLevel() {
    return universalPermissionLevel;
  }
  public void setUniversalPermissionLevel(int universalPermissionLevel) {
    this.universalPermissionLevel = universalPermissionLevel;
  }
  @Override
  public boolean equals(Object obj) {
    return obj instanceof PersistableArray && ((PersistableArray)obj).getId() == id;
  }
    public Date getLastModified() {
         //TODO: Need to implement this with the new history system
       return lastModified;
     }
    Date lastModified = new Date();
  public String toString() {
    return getId().isPersisted() ? getId().toString() : super.toString();
    }

  @Override
  public Object[] getIds() {
    fullyFetch();
    return super.getIds();
  }

  Version version;
  public Version getVersion() {
    return version;
  }

  public static void setMaxIterations(long maxIterations) {
    PersistableArray.maxIterations = maxIterations;
  }

}

TOP

Related Classes of org.persvr.data.PersistableArray$Itr

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.