From 39fce59a840b723eb013bc79285687986592b2da Mon Sep 17 00:00:00 2001
From: "Brian S. O'Neill" <bronee@gmail.com>
Date: Sun, 3 Sep 2006 06:14:41 +0000
Subject: More work on query engine.

---
 .../amazon/carbonado/repo/toy/ToyRepository.java   | 106 +++++++++
 .../carbonado/repo/toy/ToyStorableGenerator.java   | 143 ++++++++++++
 .../com/amazon/carbonado/repo/toy/ToyStorage.java  | 242 +++++++++++++++++++++
 .../amazon/carbonado/repo/toy/ToyTransaction.java  |  51 +++++
 .../amazon/carbonado/repo/toy/package-info.java    |  25 +++
 5 files changed, 567 insertions(+)
 create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java
 create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java
 create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java
 create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java
 create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/package-info.java

(limited to 'src/test/java/com/amazon/carbonado/repo')

diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java
new file mode 100644
index 0000000..159f451
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.repo.toy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.RepositoryException;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Storage;
+import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.Transaction;
+
+import com.amazon.carbonado.capability.Capability;
+
+import com.amazon.carbonado.spi.SequenceValueGenerator;
+import com.amazon.carbonado.spi.SequenceValueProducer;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyRepository implements Repository {
+    private final String mName;
+    private final Map<Class, Storage> mStorages;
+    private final Map<String, SequenceValueProducer> mSequences;
+
+    public ToyRepository() {
+        this("toy");
+    }
+
+    public ToyRepository(String name) {
+        mName = name;
+        mStorages = new HashMap<Class, Storage>();
+        mSequences = new HashMap<String, SequenceValueProducer>();
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public <S extends Storable> Storage<S> storageFor(Class<S> type)
+        throws SupportException, RepositoryException
+    {
+        synchronized (mStorages) {
+            Storage<S> storage = (Storage<S>) mStorages.get(type);
+            if (storage == null) {
+                storage = new ToyStorage<S>(this, type);
+                mStorages.put(type, storage);
+            }
+            return storage;
+        }
+    }
+
+    public Transaction enterTransaction() {
+        return new ToyTransaction();
+    }
+
+    public Transaction enterTransaction(IsolationLevel level) {
+        return enterTransaction();
+    }
+
+    public Transaction enterTopTransaction(IsolationLevel level) {
+        return enterTransaction(level);
+    }
+
+    public IsolationLevel getTransactionIsolationLevel() {
+        return null;
+    }
+
+    public <C extends Capability> C getCapability(Class<C> capabilityType) {
+        return null;
+    }
+
+    public void close() {
+    }
+
+    SequenceValueProducer getSequenceValueProducer(String name) throws RepositoryException {
+        synchronized (mSequences) {
+            SequenceValueProducer producer = mSequences.get(name);
+            if (producer == null) {
+                producer = new SequenceValueGenerator(this, name);
+                mSequences.put(name, producer);
+            }
+            return producer;
+        }
+    }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java
new file mode 100644
index 0000000..a72c7d5
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.repo.toy;
+
+import java.util.EnumSet;
+import java.util.Map;
+
+import org.cojen.classfile.ClassFile;
+import org.cojen.classfile.CodeBuilder;
+import org.cojen.classfile.MethodInfo;
+import org.cojen.classfile.Modifiers;
+import org.cojen.classfile.TypeDesc;
+
+import org.cojen.util.ClassInjector;
+import org.cojen.util.SoftValuedHashMap;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.Trigger;
+
+import com.amazon.carbonado.spi.MasterFeature;
+import com.amazon.carbonado.spi.MasterStorableGenerator;
+import com.amazon.carbonado.spi.MasterSupport;
+import com.amazon.carbonado.spi.StorableGenerator;
+import com.amazon.carbonado.spi.TriggerSupport;
+
+/**
+ * 
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyStorableGenerator<S extends Storable> {
+    private static final Map<Class, Class> cCache;
+
+    static {
+        cCache = new SoftValuedHashMap();
+    }
+
+    /**
+     * Generated class has a constructor that accepts a ToyStorage instance.
+     */
+    public static <S extends Storable> Class<? extends S> getGeneratedClass(Class<S> type)
+        throws SupportException
+    {
+        synchronized (cCache) {
+            Class<? extends S> generatedClass = (Class<? extends S>) cCache.get(type);
+            if (generatedClass != null) {
+                return generatedClass;
+            }
+            generatedClass = new ToyStorableGenerator<S>(type).generateAndInjectClass();
+            cCache.put(type, generatedClass);
+            return generatedClass;
+        }
+    }
+
+    private final Class<S> mStorableType;
+
+    private final ClassInjector mClassInjector;
+    private final ClassFile mClassFile;
+
+    private ToyStorableGenerator(Class<S> type) throws SupportException {
+        mStorableType = type;
+
+        EnumSet<MasterFeature> features = EnumSet
+            .of(MasterFeature.VERSIONING, MasterFeature.INSERT_SEQUENCES);
+
+        final Class<? extends S> abstractClass =
+            MasterStorableGenerator.getAbstractClass(mStorableType, features);
+
+        mClassInjector = ClassInjector.create(mStorableType.getName(),
+                                              abstractClass.getClassLoader());
+
+        mClassFile = new ClassFile(mClassInjector.getClassName(), abstractClass);
+        mClassFile.markSynthetic();
+        mClassFile.setSourceFile(ToyStorableGenerator.class.getName());
+        mClassFile.setTarget("1.5");
+    }
+
+    private Class<? extends S> generateAndInjectClass() {
+        TypeDesc masterSupportType = TypeDesc.forClass(MasterSupport.class);
+        TypeDesc toyStorageType = TypeDesc.forClass(ToyStorage.class);
+
+        // Add constructor that accepts a ToyStorage.
+        {
+            TypeDesc[] params = {toyStorageType};
+            MethodInfo mi = mClassFile.addConstructor(Modifiers.PUBLIC, params);
+            CodeBuilder b = new CodeBuilder(mi);
+            b.loadThis();
+            b.loadLocal(b.getParameter(0));
+            b.invokeSuperConstructor(new TypeDesc[] {masterSupportType});
+            b.returnVoid();
+        }
+
+        // Implement abstract methods which all delegate to ToyStorage instance.
+
+        generateDelegatedMethod
+            (MasterStorableGenerator.DO_TRY_LOAD_MASTER_METHOD_NAME, "doTryLoad");
+        generateDelegatedMethod
+            (MasterStorableGenerator.DO_TRY_INSERT_MASTER_METHOD_NAME, "doTryInsert");
+        generateDelegatedMethod
+            (MasterStorableGenerator.DO_TRY_UPDATE_MASTER_METHOD_NAME, "doTryUpdate");
+        generateDelegatedMethod
+            (MasterStorableGenerator.DO_TRY_DELETE_MASTER_METHOD_NAME, "doTryDelete");
+
+        Class<? extends S> generatedClass = mClassInjector.defineClass(mClassFile);
+
+        return generatedClass;
+    }
+
+    private void generateDelegatedMethod(String masterMethodName, String supportMethodName) {
+        TypeDesc triggerSupportType = TypeDesc.forClass(TriggerSupport.class);
+        TypeDesc toyStorageType = TypeDesc.forClass(ToyStorage.class);
+
+        TypeDesc[] storableParam = {TypeDesc.forClass(Storable.class)};
+
+        MethodInfo mi = mClassFile.addMethod
+            (Modifiers.PROTECTED, masterMethodName, TypeDesc.BOOLEAN, null);
+        CodeBuilder b = new CodeBuilder(mi);
+
+        b.loadThis();
+        b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType);
+        b.checkCast(toyStorageType);
+        b.loadThis();
+        b.invokeVirtual(toyStorageType, supportMethodName, TypeDesc.BOOLEAN, storableParam);
+        b.returnValue(TypeDesc.BOOLEAN);
+    }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java
new file mode 100644
index 0000000..dd29e4b
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.repo.toy;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.PersistException;
+import com.amazon.carbonado.Query;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.RepositoryException;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Storage;
+import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.Transaction;
+import com.amazon.carbonado.Trigger;
+
+import com.amazon.carbonado.spi.MasterSupport;
+import com.amazon.carbonado.spi.SequenceValueProducer;
+
+import com.amazon.carbonado.util.QuickConstructorGenerator;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIntrospector;
+
+import com.amazon.carbonado.qe.FilteredQueryExecutor;
+import com.amazon.carbonado.qe.IterableQueryExecutor;
+import com.amazon.carbonado.qe.QueryExecutor;
+import com.amazon.carbonado.qe.SortedQueryExecutor;
+import com.amazon.carbonado.qe.StandardQuery;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyStorage<S extends Storable> implements Storage<S>, MasterSupport<S> {
+    final ToyRepository mRepo;
+    final Class<S> mType;
+
+    final InstanceFactory mInstanceFactory;
+
+    final Collection<S> mData;
+    final Lock mDataLock;
+
+    public ToyStorage(ToyRepository repo, Class<S> type) throws SupportException {
+        StorableIntrospector.examine(type);
+        mRepo = repo;
+        mType = type;
+
+        Class<? extends S> generatedStorableClass = ToyStorableGenerator.getGeneratedClass(type);
+        mInstanceFactory = QuickConstructorGenerator
+            .getInstance(generatedStorableClass, InstanceFactory.class);
+
+        mData = new LinkedList<S>();
+        mDataLock = new ReentrantLock();
+    }
+
+    public Class<S> getStorableType() {
+        return mType;
+    }
+
+    public S prepare() {
+        return (S) mInstanceFactory.instantiate(this);
+    }
+
+    public Query<S> query() throws FetchException {
+        return new ToyQuery(null);
+    }
+
+    public Query<S> query(String filter) throws FetchException {
+        return query(Filter.filterFor(mType, filter));
+    }
+
+    public Query<S> query(Filter<S> filter) throws FetchException {
+        return new ToyQuery(filter.initialFilterValues());
+    }
+
+    public boolean addTrigger(Trigger<? super S> trigger) {
+        return false;
+    }
+
+    public boolean removeTrigger(Trigger<? super S> trigger) {
+        return false;
+    }
+
+    public boolean doTryLoad(S storable) {
+        mDataLock.lock();
+        try {
+            for (S existing : mData) {
+                if (existing.equalPrimaryKeys(storable)) {
+                    storable.markAllPropertiesDirty();
+                    existing.copyAllProperties(storable);
+                    storable.markAllPropertiesClean();
+                    return true;
+                }
+            }
+            return false;
+        } finally {
+            mDataLock.unlock();
+        }
+    }
+
+    public boolean doTryInsert(S storable) {
+        mDataLock.lock();
+        try {
+            for (S existing : mData) {
+                if (existing.equalPrimaryKeys(storable)) {
+                    return false;
+                }
+            }
+            storable.markAllPropertiesClean();
+            mData.add((S) storable.copy());
+            return true;
+        } finally {
+            mDataLock.unlock();
+        }
+    }
+
+    public boolean doTryUpdate(S storable) {
+        mDataLock.lock();
+        try {
+            for (S existing : mData) {
+                if (existing.equalPrimaryKeys(storable)) {
+                    existing.markAllPropertiesDirty();
+                    storable.copyAllProperties(existing);
+                    existing.markAllPropertiesClean();
+                    existing.copyAllProperties(storable);
+                    storable.markAllPropertiesClean();
+                    return true;
+                }
+            }
+            return false;
+        } finally {
+            mDataLock.unlock();
+        }
+    }
+
+    public boolean doTryDelete(S storable) {
+        mDataLock.lock();
+        try {
+            Iterator<S> it = mData.iterator();
+            while (it.hasNext()) {
+                S existing = it.next();
+                if (existing.equalPrimaryKeys(storable)) {
+                    it.remove();
+                    return true;
+                }
+            }
+            return false;
+        } finally {
+            mDataLock.unlock();
+        }
+    }
+
+    public Repository getRootRepository() {
+        return mRepo;
+    }
+
+    public boolean isPropertySupported(String propertyName) {
+        return StorableIntrospector.examine(mType)
+            .getAllProperties().containsKey(propertyName);
+    }
+
+    public Trigger<? super S> getInsertTrigger() {
+        return null;
+    }
+
+    public Trigger<? super S> getUpdateTrigger() {
+        return null;
+    }
+
+    public Trigger<? super S> getDeleteTrigger() {
+        return null;
+    }
+
+    public SequenceValueProducer getSequenceValueProducer(String name) throws PersistException {
+        try {
+            return mRepo.getSequenceValueProducer(name);
+        } catch (RepositoryException e) {
+            throw e.toPersistException();
+        }
+    }
+
+    public static interface InstanceFactory {
+        Storable instantiate(ToyStorage storage);
+    }
+
+    private class ToyQuery extends StandardQuery<S> {
+        ToyQuery(FilterValues<S> values, String... orderings) {
+            super(values, orderings);
+        }
+
+        protected Storage<S> getStorage() {
+            return ToyStorage.this;
+        }
+
+        protected Transaction enterTransactionForDelete(IsolationLevel level) {
+            return mRepo.enterTransaction(level);
+        }
+
+        protected QueryExecutor<S> getExecutor(FilterValues<S> values, String... orderings) {
+            QueryExecutor<S> executor = new IterableQueryExecutor<S>(mType, mData, mDataLock);
+
+            if (values != null) {
+                executor = new FilteredQueryExecutor<S>(executor, values.getFilter());
+            }
+
+            // FIXME: sorting
+
+            return executor;
+        }
+
+        protected StandardQuery<S> newInstance(FilterValues<S> values, String... orderings) {
+            return new ToyQuery(values, orderings);
+        }
+    }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java
new file mode 100644
index 0000000..c16d1f1
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.repo.toy;
+
+import java.util.concurrent.TimeUnit;
+
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.PersistException;
+import com.amazon.carbonado.Transaction;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyTransaction implements Transaction {
+    public void commit() throws PersistException {
+    }
+
+    public void exit() throws PersistException {
+    }
+
+    public void setForUpdate(boolean forUpdate) {
+    }
+
+    public boolean isForUpdate() {
+        return false;
+    }
+
+    public void setDesiredLockTimeout(int timeout, TimeUnit unit) {
+    }
+
+    public IsolationLevel getIsolationLevel() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/package-info.java b/src/test/java/com/amazon/carbonado/repo/toy/package-info.java
new file mode 100644
index 0000000..769ea8b
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/repo/toy/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Standalone toy repository implementation. This repository is only suitable
+ * for running tests that don't require anything sophisticated. It doesn't
+ * support transactions, nothing is actually persisted, and all queries do full
+ * scans. The repository is thread-safe, however.
+ */
+package com.amazon.carbonado.repo.toy;
-- 
cgit v1.2.3