/* * Copyright 2006-2012 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.qe; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; import java.util.AbstractList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.amazon.carbonado.Storable; import com.amazon.carbonado.info.ChainedProperty; import com.amazon.carbonado.info.OrderedProperty; import com.amazon.carbonado.info.StorableIntrospector; import com.amazon.carbonado.util.SoftValuedCache; /** * Produces unmodifiable lists of {@link OrderedProperty orderings}. Instances * are immutable, canonical and cached. Calls to "equals" and "hashCode" are * fast. * * @author Brian S O'Neill */ public class OrderingList<S extends Storable> extends AbstractList<OrderedProperty<S>> implements Serializable { private static final long serialVersionUID = 3692335128299485356L; private static final OrderingList EMPTY_LIST = new OrderingList(); private static final SoftValuedCache<Class, OrderingList> cCache; static { cCache = SoftValuedCache.newCache(11); } /** * Returns a canonical empty instance. */ public static <S extends Storable> OrderingList<S> emptyList() { return EMPTY_LIST; } /** * Returns a canonical instance composed of the given ordering. * * @throws IllegalArgumentException if ordering property is not in S */ public static <S extends Storable> OrderingList<S> get(Class<S> type, String property) { OrderingList<S> list = emptyList(); if (property != null) { list = list.concat(type, property); } return list; } /** * Returns a canonical instance composed of the given orderings. * * @throws IllegalArgumentException if any ordering property is not in S */ public static <S extends Storable> OrderingList<S> get(Class<S> type, String... orderings) { OrderingList<S> list = emptyList(); if (orderings != null && orderings.length > 0) { for (String property : orderings) { list = list.concat(type, property); } } return list; } /** * Returns a canonical instance composed of the given orderings. */ public static <S extends Storable> OrderingList<S> get(OrderedProperty<S>... orderings) { OrderingList<S> list = emptyList(); if (orderings != null && orderings.length > 0) { for (OrderedProperty<S> property : orderings) { list = list.concat(property); } } return list; } /** * Returns a canonical instance composed of the given orderings. */ public static <S extends Storable> OrderingList<S> get(List<OrderedProperty<S>> orderings) { OrderingList<S> list = emptyList(); if (orderings != null && orderings.size() > 0) { for (OrderedProperty<S> property : orderings) { list = list.concat(property); } } return list; } private static <S extends Storable> OrderingList<S> getListHead(Class<S> type) { OrderingList<S> node; synchronized (cCache) { node = (OrderingList<S>) cCache.get(type); if (node == null) { node = new OrderingList<S>(); cCache.put(type, node); } } return node; } private final OrderingList<S> mParent; private final OrderedProperty<S> mProperty; private final int mSize; private Map<Object, OrderingList<S>> mNextNode; private OrderedProperty<S>[] mOrderings; private String[] mOrderingStrings; private OrderingList() { mParent = null; mProperty = null; mSize = 0; } private OrderingList(OrderingList<S> parent, OrderedProperty<S> property) { if (property == null) { throw new IllegalArgumentException("Ordering property is null"); } mParent = parent; mProperty = property; mSize = parent.mSize + 1; } @Override public int size() { return mSize; } @Override public OrderedProperty<S> get(int index) { return asArray()[index]; } /** * Returns a list which concatenates this one with the given property. */ public OrderingList<S> concat(Class<S> type, String property) { OrderingList<S> newList = this; if (newList == EMPTY_LIST) { // Cannot concat from singleton EMPTY_LIST. newList = getListHead(type); } return newList.nextNode(type, property); } /** * Returns a list which concatenates this one with the given property. */ public OrderingList<S> concat(OrderedProperty<S> property) { OrderingList<S> newList = this; if (newList == EMPTY_LIST) { // Cannot concat from singleton EMPTY_LIST. newList = getListHead (property.getChainedProperty().getPrimeProperty().getEnclosingType()); } return newList.nextNode(property); } /** * Returns a list which concatenates this one with the other one. */ public OrderingList<S> concat(OrderingList<S> other) { if (size() == 0) { return other; } OrderingList<S> newList = this; if (other.size() > 0) { for (OrderedProperty<S> property : other) { newList = newList.concat(property); } } return newList; } /** * Eliminates redundant ordering properties. */ public OrderingList<S> reduce() { if (size() == 0) { return this; } Set<ChainedProperty<S>> seen = new HashSet<ChainedProperty<S>>(); OrderingList<S> newList = emptyList(); for (OrderedProperty<S> property : this) { ChainedProperty<S> chained = property.getChainedProperty(); if (!seen.contains(chained)) { newList = newList.concat(property); seen.add(chained); } } return newList; } /** * Returns this list with all orderings in reverse. */ public OrderingList<S> reverseDirections() { if (size() == 0) { return this; } OrderingList<S> reversedList = emptyList(); for (int i=0; i<size(); i++) { reversedList = reversedList.concat(get(i).reverse()); } return reversedList; } /** * Returns a list with the given element replaced. */ public OrderingList<S> replace(int index, OrderedProperty<S> property) { int size = size(); if (index < 0 || index >= size) { throw new IndexOutOfBoundsException(); } OrderingList<S> newList = emptyList(); for (int i=0; i<size; i++) { newList = newList.concat(i == index ? property : get(i)); } return newList; } @Override public OrderingList<S> subList(int fromIndex, int toIndex) { // Check for optimization opportunity. if (fromIndex == 0 && toIndex >= 0 && toIndex <= mSize) { if (toIndex == 0) { return emptyList(); } OrderingList<S> list = this; while (toIndex < list.mSize) { list = list.mParent; } return list; } return get(super.subList(fromIndex, toIndex)); } /** * This method is not public because the array is not a clone. */ OrderedProperty<S>[] asArray() { if (mOrderings == null) { OrderedProperty<S>[] orderings = new OrderedProperty[mSize]; OrderingList<S> node = this; for (int i=mSize; --i>=0; ) { orderings[i] = node.mProperty; node = node.mParent; } mOrderings = orderings; } return mOrderings; } /** * Returns the orderings as qualified string property names. Each is * prefixed with a '+' or '-'. * * <p>This method is not public because the array is not a clone. */ String[] asStringArray() { if (mOrderingStrings == null) { String[] orderings = new String[mSize]; OrderingList<S> node = this; for (int i=mSize; --i>=0; ) { orderings[i] = node.mProperty.toString(); node = node.mParent; } mOrderingStrings = orderings; } return mOrderingStrings; } @Override public int hashCode() { return System.identityHashCode(this); } @Override public boolean equals(Object other) { if (this == other) { return true; } return super.equals(other); } private synchronized OrderingList<S> nextNode(Class<S> type, String property) { OrderingList<S> node; if (mNextNode == null) { mNextNode = new HashMap<Object, OrderingList<S>>(); node = null; } else { node = mNextNode.get(property); } if (node == null) { OrderedProperty<S> op = OrderedProperty .parse(StorableIntrospector.examine(type), property); node = nextNode(op); mNextNode.put(property, node); } return node; } private synchronized OrderingList<S> nextNode(OrderedProperty<S> property) { OrderingList<S> node; if (mNextNode == null) { mNextNode = new HashMap<Object, OrderingList<S>>(); node = null; } else { node = mNextNode.get(property); } if (node == null) { node = new OrderingList<S>(this, property); mNextNode.put(property, node); } return node; } private Object writeReplace() { return new Orderings(asArray()); } private static class Orderings implements Externalizable { private static final long serialVersionUID = 1L; private OrderedProperty[] mOrderings; // Required for Externalizable. public Orderings() { } Orderings(OrderedProperty<?>[] orderings) { mOrderings = orderings; } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(mOrderings); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { mOrderings = (OrderedProperty<?>[]) in.readObject(); } private Object readResolve() { return OrderingList.get(mOrderings); } } }