/* * 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 extends AbstractList> implements Serializable { private static final long serialVersionUID = 3692335128299485356L; private static final OrderingList EMPTY_LIST = new OrderingList(); private static final SoftValuedCache cCache; static { cCache = SoftValuedCache.newCache(11); } /** * Returns a canonical empty instance. */ public static OrderingList emptyList() { return EMPTY_LIST; } /** * Returns a canonical instance composed of the given ordering. * * @throws IllegalArgumentException if ordering property is not in S */ public static OrderingList get(Class type, String property) { OrderingList 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 OrderingList get(Class type, String... orderings) { OrderingList 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 OrderingList get(OrderedProperty... orderings) { OrderingList list = emptyList(); if (orderings != null && orderings.length > 0) { for (OrderedProperty property : orderings) { list = list.concat(property); } } return list; } /** * Returns a canonical instance composed of the given orderings. */ public static OrderingList get(List> orderings) { OrderingList list = emptyList(); if (orderings != null && orderings.size() > 0) { for (OrderedProperty property : orderings) { list = list.concat(property); } } return list; } private static OrderingList getListHead(Class type) { OrderingList node; synchronized (cCache) { node = (OrderingList) cCache.get(type); if (node == null) { node = new OrderingList(); cCache.put(type, node); } } return node; } private final OrderingList mParent; private final OrderedProperty mProperty; private final int mSize; private Map> mNextNode; private OrderedProperty[] mOrderings; private String[] mOrderingStrings; private OrderingList() { mParent = null; mProperty = null; mSize = 0; } private OrderingList(OrderingList parent, OrderedProperty 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 get(int index) { return asArray()[index]; } /** * Returns a list which concatenates this one with the given property. */ public OrderingList concat(Class type, String property) { OrderingList 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 concat(OrderedProperty property) { OrderingList 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 concat(OrderingList other) { if (size() == 0) { return other; } OrderingList newList = this; if (other.size() > 0) { for (OrderedProperty property : other) { newList = newList.concat(property); } } return newList; } /** * Eliminates redundant ordering properties. */ public OrderingList reduce() { if (size() == 0) { return this; } Set> seen = new HashSet>(); OrderingList newList = emptyList(); for (OrderedProperty property : this) { ChainedProperty 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 reverseDirections() { if (size() == 0) { return this; } OrderingList reversedList = emptyList(); for (int i=0; i replace(int index, OrderedProperty property) { int size = size(); if (index < 0 || index >= size) { throw new IndexOutOfBoundsException(); } OrderingList newList = emptyList(); for (int i=0; i subList(int fromIndex, int toIndex) { // Check for optimization opportunity. if (fromIndex == 0 && toIndex >= 0 && toIndex <= mSize) { if (toIndex == 0) { return emptyList(); } OrderingList 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[] asArray() { if (mOrderings == null) { OrderedProperty[] orderings = new OrderedProperty[mSize]; OrderingList 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 '-'. * *

This method is not public because the array is not a clone. */ String[] asStringArray() { if (mOrderingStrings == null) { String[] orderings = new String[mSize]; OrderingList 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 nextNode(Class type, String property) { OrderingList node; if (mNextNode == null) { mNextNode = new HashMap>(); node = null; } else { node = mNextNode.get(property); } if (node == null) { OrderedProperty op = OrderedProperty .parse(StorableIntrospector.examine(type), property); node = nextNode(op); mNextNode.put(property, node); } return node; } private synchronized OrderingList nextNode(OrderedProperty property) { OrderingList node; if (mNextNode == null) { mNextNode = new HashMap>(); node = null; } else { node = mNextNode.get(property); } if (node == null) { node = new OrderingList(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); } } }