
/*
 * Copyright (C) 2014 Archie L. Cobbs. All rights reserved.
 *
 * $Id$
 */

package org.jsimpledb;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.UncheckedExecutionException;

import java.util.concurrent.ExecutionException;

/**
 * Caches parsed {@link ReferencePath}s.
 */
class ReferencePathCache {

    private final JSimpleDB jdb;
    private final LoadingCache<Key, ReferencePath> cache;

    /**
     * Constructor.
     *
     * @param jdb {@link JSimpleDB} against which to resolve object and field names
     * @throws IllegalArgumentException if {@code jdb} is null
     */
    ReferencePathCache(JSimpleDB jdb) {
        if (jdb == null)
            throw new IllegalArgumentException("null jdb");
        this.jdb = jdb;
        this.cache = CacheBuilder.newBuilder().softValues().build(new CacheLoader<Key, ReferencePath>() {
            public ReferencePath load(Key key) {
                return new ReferencePath(ReferencePathCache.this.jdb, key.getStartType(), key.getPath(), key.isLastIsSubField());
            }
        });
    }

    /**
     * Get/create a {@link ReferencePath}.
     *
     * @see ReferencePath#ReferencePath
     */
    public ReferencePath get(TypeToken<?> startType, String path, Boolean lastIsSubField) {
        Throwable cause;
        try {
            return this.cache.get(new Key(startType, path, lastIsSubField));
        } catch (ExecutionException e) {
            cause = e.getCause() != null ? e.getCause() : e;
        } catch (UncheckedExecutionException e) {
            cause = e.getCause() != null ? e.getCause() : e;
        }
        if (cause instanceof Error)
            throw (Error)cause;
        if (cause instanceof RuntimeException)
            throw (RuntimeException)cause;
        throw new RuntimeException("internal error", cause);
    }

// Key

    private static class Key {

        private final TypeToken<?> startType;
        private final String path;
        private final Boolean lastIsSubField;

        Key(TypeToken<?> startType, String path, Boolean lastIsSubField) {
            this.startType = startType;
            this.path = path;
            this.lastIsSubField = lastIsSubField;
        }

        public TypeToken<?> getStartType() {
            return this.startType;
        }

        public String getPath() {
            return this.path;
        }

        public Boolean isLastIsSubField() {
            return this.lastIsSubField;
        }
    }
}

@@ -2613,21 +2613,717 @@
 d;%0A        %7D
+%0A%0A        @Override%0A        public boolean equals(Object obj) %7B%0A            if (obj == this)%0A                return true;%0A            if (obj == null %7C%7C obj.getClass() != this.getClass())%0A                return false;%0A            final Key that = (Key)obj;%0A            return this.startType.equals(that.startType) && this.path.equals(that.path)%0A              && (this.lastIsSubField != null ? this.lastIsSubField.equals(that.lastIsSubField) : that.lastIsSubField == null);%0A        %7D%0A%0A        @Override%0A        public int hashCode() %7B%0A            return this.startType.hashCode() %5E this.path.hashCode()%0A              %5E (this.lastIsSubField != null ? this.lastIsSubField.hashCode() : -1);%0A        %7D
 %0A    %7D%0A%7D%0A

Add missing equals() and hashCode() implementations.