/* Copyright (c) 2008 Google Inc. * * 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.google.gdata.model; import com.google.gdata.util.common.base.Preconditions; /** * A key to lookup a particular metadata configuration. This takes into account * static information such as the parent class of the metadata and any metadata * subtypes, as well as including the normal {@link MetadataContext} contextual * information. * * */ final class TransformKey implements Comparable<TransformKey> { /** * Creates a metadata key for the given parent class. Will return null if * {@code parentClass} is null. * * @param parent the parent to create a transform for. * @return a key with just the parent class set, or null if the parent class * was null. */ static TransformKey forParent(ElementKey<?, ?> parent, MetadataKey<?> key) { return forTransform(parent, key, null); } /** * Creates a metadata key for the given subtype. Will return null if * {@code subType} is null. * * @param key the metadata key to create a transform for. * @return a key with just the subtype set, or null if the subtype was null. */ static TransformKey forKey(MetadataKey<?> key) { return forTransform(null, key, null); } /** * Creates a metadata key for the given metadata context. Will return null if * {@code context} is null. * * @param context the metadata context to create a key for. * @return a key with just the metadata context set, or null if the context * was null. */ static TransformKey forContext(MetadataKey<?> key, MetadataContext context) { return forTransform(null, key, context); } /** * Creates a metadata key for the given parentClass, subType, and context. * If all parts of the key are null, this method will return null, which is * the default key. * * @param parent the parent key to create a transform for * @param key the key to create a transform for * @param context the metadata context to create a transform for * @return a transform key with the given parts set, or null if all were null. */ static TransformKey forTransform(ElementKey<?, ?> parent, MetadataKey<?> key, MetadataContext context) { Preconditions.checkNotNull(key, "key"); return new TransformKey(parent, key, context); } private final ElementKey<?, ?> parent; private final MetadataKey<?> key; private final MetadataContext context; /** * Private constructor, only factory methods can be used to create keys. This * enforces that {@code null} is the default key, and in the future could be * used to cache keys to allow identity semantics for equals. */ private TransformKey(ElementKey<?, ?> parent, MetadataKey<?> key, MetadataContext context) { this.parent = parent; this.key = key; this.context = context; } /** * Returns true if this key matches the given key. A key is considered a * match for another key if the parent and subtypes are matches or subtypes of * the given key's parent and subtype, and the context is a subset of the * matched context. */ boolean matches(TransformKey other) { return (parent == null || parent.matches(other.parent)) && (key.matches(other.key)) && (context == null || context.matches(other.context)); } /** * Merged this key with another key. This will override local values if the * given key has a value for that part of the key. */ TransformKey bind(TransformKey other) { if (other == null) { return this; } ElementKey<?, ?> otherParent = other.getParent(); MetadataKey<?> otherKey = other.getKey(); MetadataContext otherContext = other.getContext(); if ((otherParent == parent) && (otherKey == key) && ((otherContext != null && otherContext.equals(context)) || (otherContext == null && context == null))) { return this; } return new TransformKey( otherParent != null ? otherParent : parent, otherKey != null ? otherKey : key, otherContext != null ? otherContext : context); } /** * Gets the parent portion of the key. This represents a parent-child * metadata relationship, and is used to store the metadata delta associated * with that relationship. * * @return the parent class or {@code null}. */ ElementKey<?, ?> getParent() { return parent; } /** * Gets the subtype portion of the key. This allows metadata modifications * to be associated with a particular subtype of element in the tree. * * @return the subtype class or {@code null}. */ MetadataKey<?> getKey() { return key; } /** * Gets the context portion of the key. This represents the request-scoped * portions of the key, and is used to create the metadata for a particular * request. * * @return the context portion of the key, or {@code null}. */ MetadataContext getContext() { return context; } /** * Compares this transform key to the other transform key. Transform keys are * sorted by: * <ul> * <li>Parent key</li> * <li>Key</li> * <li>Context</li> * </ul> * * <p>A null value for any part is assumed to be the lowest value. */ public int compareTo(TransformKey other) { if (this == other) { return 0; } if (other == null) { return 1; } int compare = compare(parent, other.parent); if (compare != 0) { return compare; } compare = compare(key, other.key); if (compare != 0) { return compare; } if (context == null) { if (other.context != null) { compare = -1; } } else if (other.context == null) { compare = 1; } else { compare = context.compareTo(other.context); } return compare; } /** * Order two metadata keys, first checking nulls. Nulls count as lower * than any other value, so more general transforms will show up first. */ static int compare(MetadataKey<?> a, MetadataKey<?> b) { if (a == b) { return 0; } if (a == null) { return -1; } if (b == null) { return 1; } return a.compareTo(b); } @Override public int hashCode() { int hash = key.hashCode(); hash *= 17; if (parent != null) { hash += parent.hashCode(); } hash *= 17; if (context != null) { hash += context.hashCode(); } return hash; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TransformKey)) { return false; } TransformKey other = (TransformKey) obj; if (parent == null) { if (other.parent != null) { return false; } } else if (!parent.equals(other.parent)) { return false; } if (!key.equals(other.key)) { return false; } if (context == null) { if (other.context != null) { return false; } } else if (!context.equals(other.context)) { return false; } return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{TransformKey("); sb.append(parent == null ? "null" : parent); sb.append(','); sb.append(key == null ? "null" : key); sb.append(','); sb.append(context); sb.append(")}"); return sb.toString(); } }