package com.thinkaurelius.titan.graphdb.types.vertices;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.thinkaurelius.titan.core.Parameter;
import com.thinkaurelius.titan.core.TitanKey;
import com.thinkaurelius.titan.graphdb.transaction.StandardTitanTx;
import com.thinkaurelius.titan.graphdb.types.IndexDefinition;
import com.thinkaurelius.titan.graphdb.types.IndexParameters;
import com.thinkaurelius.titan.graphdb.types.IndexType;
import com.thinkaurelius.titan.graphdb.types.TypeAttributeType;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.Vertex;

import javax.annotation.Nullable;
import java.util.List;

public class TitanKeyVertex extends TitanTypeVertex implements TitanKey {

    public TitanKeyVertex(StandardTitanTx tx, long id, byte lifecycle) {
        super(tx, id, lifecycle);
    }

    @Override
    public Class<?> getDataType() {
        return getDefinition().getValue(TypeAttributeType.DATATYPE,Class.class);
    }

    @Override
    public final boolean isPropertyKey() {
        return true;
    }

    @Override
    public final boolean isEdgeLabel() {
        return false;
    }

    private IndexDefinition[] getIndexes() {
        if (indexes==null) {
            IndexType[] indexTypes = getDefinition().getValue(TypeAttributeType.INDEXES,IndexType[].class);
            IndexParameters[] indexParas = getDefinition().getValue(TypeAttributeType.INDEX_PARAMETERS,IndexParameters[].class);
            Preconditions.checkArgument(indexTypes!=null,"Missing index types!");
            if (indexParas==null) { //Default initialization to no parameters
                indexParas = new IndexParameters[indexTypes.length];
                for (int i=0;i<indexTypes.length;i++)
                    indexParas[i]=new IndexParameters(indexTypes[i].getIndexName(),new Parameter[0]);
            }
            Preconditions.checkArgument(indexTypes.length==indexParas.length,"Lengths don't agree: %s vs %s",indexTypes.length,indexParas.length);
            indexes = new IndexDefinition[indexTypes.length];
            for (int i=0;i<indexTypes.length;i++) {
                indexes[i]=IndexDefinition.of(indexTypes[i],indexParas[i]);
            }
        }
        Preconditions.checkNotNull(indexes);
        return indexes;
    }

    private transient IndexDefinition[] indexes;
    private transient List<IndexDefinition> vertexIndexes;
    private transient List<IndexDefinition> edgeIndexes;

    @Override
    public Iterable<String> getIndexes(Class<? extends Element> clazz) {
        if (clazz==Vertex.class || clazz==Edge.class) {
            if (getIndexList(clazz).isEmpty()) return ImmutableList.of();
            else return Iterables.transform(getIndexList(clazz), new Function<IndexDefinition, String>() {
                @Nullable
                @Override
                public String apply(@Nullable IndexDefinition indexType) {
                    return indexType.getIndexName();
                }
            });
        } else if (clazz == Element.class) return Iterables.concat(getIndexes(Vertex.class),getIndexes(Edge.class));
        else throw new IllegalArgumentException("Unexpected element type: " + clazz);
    }

    @Override
    public boolean hasIndex(String name, Class<? extends Element> elementType) {
        return getIndex(name,elementType)!=null;
    }

    public IndexDefinition getIndex(String name, Class<? extends Element> elementType) {
        Preconditions.checkArgument(elementType==Vertex.class || elementType==Edge.class, "Expected Vertex or Edge class as argument");
        for (int i=0;i<getIndexes().length;i++) {
            IndexDefinition def = getIndexes()[i];
            if (def.getElementType()==elementType && def.getIndexName().equals(name)) return def;
        }
        return null;
    }

    private List<IndexDefinition> getIndexList(Class<? extends Element> type) {
        Preconditions.checkArgument(type==Vertex.class || type==Edge.class, "Expected Vertex or Edge class as argument");
        List<IndexDefinition> result = type==Vertex.class?vertexIndexes:edgeIndexes;
        if (result==null) {
            //Build it
            ImmutableList.Builder b = new ImmutableList.Builder();
            for (IndexDefinition it : getIndexes()) if (type.isAssignableFrom(it.getElementType())) b.add(it);
            result = b.build();
            if (type==Vertex.class) vertexIndexes=result;
            else if (type==Edge.class) edgeIndexes=result;
        }
        return result;
    }

}

@@ -786,16 +786,17 @@
 llable;%0A
+%0A
 import j
@@ -2176,23 +2176,37 @@
         
-i
+I
 ndex
-es
+Definition tmp%5B%5D
  = new I
@@ -2308,23 +2308,19 @@
         
-indexes
+tmp
 %5Bi%5D=Inde
@@ -2370,32 +2370,59 @@
 ;%0A            %7D%0A
+            indexes = tmp;%0A
         %7D%0A      
@@ -2495,32 +2495,41 @@
  %7D%0A%0A    private 
+volatile 
 transient IndexD
@@ -2553,32 +2553,41 @@
 es;%0A    private 
+volatile 
 transient List%3CI
@@ -2629,16 +2629,25 @@
 private 
+volatile 
 transien
@@ -4501,16 +4501,32 @@
 dexes())
+%0A               
  if (typ
@@ -4565,16 +4565,36 @@
 Type()))
+%0A                   
  b.add(i

Fix concurrent lazy init bug in TitanKeyVertex