/* 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.Version; import com.google.gdata.wireformats.AltFormat; /** * A context that metadata is operating under. Currently this contains the * alt format, projection and version of the current request. Immutable. * * <p>This class also contains static initializers for the transforms required * by each of our alt formats, which guarantees they will be loaded. We may * want to put them somewhere else, but we need to move the constants for the * contexts along with them. * * */ public final class MetadataContext implements Comparable<MetadataContext> { /** The ATOM metadata context. */ public static final MetadataContext ATOM = MetadataContext.forAlt( AltFormat.ATOM); /** The RSS metadata context. */ public static final MetadataContext RSS = MetadataContext.forAlt( AltFormat.RSS); /** * Creates a new immutable metadata context with just an alt format. The * format must not be null or this will throw a null pointer exception. * * @param format the alt format for the context, not {@code null}. * @return a metadata context for the alt format. */ public static MetadataContext forAlt(AltFormat format) { return forContext(format, null, null); } /** * Creates a new immutable metadata context with just projection. If the * projection is null this will return null, which is the default context. * * @param projection the projection of the context. * @return an immutable metadata context with the given projection, or null * if the projection was null. */ public static MetadataContext forProjection(String projection) { return forContext(null, projection, null); } /** * Constructs a new immutable metadata context with just version. If version * is null this will return null, which is the default context. * * @param version the version of the context. * @return an immutable metadata context with the given version, or null if * the version was null. */ public static MetadataContext forVersion(Version version) { return forContext(null, null, version); } /** * Creates a new immutable metadata context. If version, projection and alt * type are all null this method will return null, which is the default * context. * * @param format the alt format of the context. * @param projection the projection for the context. * @param version the version of the context. * @return an immutable metadata context with the given alt type, projection * and version, or null if all parameters are null. */ public static MetadataContext forContext(AltFormat format, String projection, Version version) { if (format == null && projection == null && version == null) { return null; } return new MetadataContext(format, projection, version); } // The alt type of the request. private final AltFormat altFormat; // The projection of the request. private final String projection; // The version of the request. private final Version version; /** * Private constructor, callers must use the static factory methods. */ private MetadataContext( AltFormat format, String projection, Version version) { this.altFormat = format; this.projection = projection; this.version = version; } /** * Returns true if this context is a match for the given context. A context * is a match for another context if it is a subset of that context. Null * properties are ignored, but any properties that are set must match the * same property on the other context. */ public boolean matches(MetadataContext other) { return other != null && (altFormat == null || altFormat.equals(other.altFormat)) && (projection == null || projection.equals(other.projection)) && (version == null || (other.version != null && other.version.isCompatible(version))); } /** * The alt format the context represents. * * @return the alt format or null if the context doesn't have an alt format. */ public AltFormat getAltFormat() { return altFormat; } /** * The projection the context represents. * * @return the projection or null if the context doesn't have a projection. */ public String getProjection() { return projection; } /** * The version the context represents. * * @return the version or null if no version exists in this context. */ public Version getVersion() { return version; } /** * Compare two context objects to order them. A null value for any field is * treated as a low value. Otherwise we use the normal string comparison. If * the given context object is {@code null} a {@link NullPointerException} * will be thrown. */ public int compareTo(MetadataContext other) { if (this == other) { return 0; } int compare = compareAltFormat(altFormat, other.altFormat); if (compare != 0) { return compare; } compare = compareString(projection, other.projection); if (compare != 0) { return compare; } // Compare versions. return compareVersion(version, other.version); } /** * Compares two alt formats, where either may be null. Just compares them * based on their names. */ static int compareAltFormat(AltFormat a, AltFormat b) { return compareString( (a == null) ? null : a.getName(), (b == null) ? null : b.getName()); } /** * Compares two strings, where either may be null. */ static int compareString(String a, String b) { if (a == b) { return 0; } if (a == null) { return -1; } if (b == null) { return 1; } return a.compareTo(b); } /** * Compares two versions, where either may be null. */ static int compareVersion(Version a, Version b) { if (a == b) { return 0; } if (a == null) { return -1; } if (b == null) { return 1; } int compare = MetadataKey.compareClass( a.getServiceClass(), b.getServiceClass()); if (compare != 0) { return compare; } int aMajor = a.getMajor(); int bMajor = b.getMajor(); if (aMajor != bMajor) { return aMajor < bMajor ? -1 : 1; } int aMinor = a.getMinor(); int bMinor = b.getMinor(); return aMinor < bMinor ? -1 : (aMinor == bMinor ? 0 : 1); } /** * The hashCode for a context is just the hashcode of its parts. */ @Override public int hashCode() { int hash = 0; if (altFormat != null) { hash += altFormat.hashCode(); } if (projection != null) { hash = hash * 37; hash += projection.hashCode(); } if (version != null) { hash = hash * 37; hash += version.hashCode(); } return hash; } /** * Checks that the other object is a metadata context with the same alt type * and version. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MetadataContext)) { return false; } MetadataContext other = (MetadataContext) obj; if (altFormat == null) { if (other.altFormat != null) { return false; } } else if (!altFormat.equals(other.altFormat)) { return false; } if (projection == null) { if (other.projection != null) { return false; } } else if (!projection.equals(other.projection)) { return false; } if (version == null) { if (other.version != null) { return false; } } else if (!version.equals(other.version)) { return false; } return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("{MetadataContext("); sb.append(altFormat); sb.append(','); sb.append(projection); sb.append(','); sb.append(version); sb.append(")}"); return sb.toString(); } }