/* 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; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentMap; /** * An immutable registry storing the information for a single attribute, based * on its ID. All transforms around the attribute with this ID are stored in * a single registry. The attribute registry is able to bind to a particular * {@link TransformKey} in order to reference a single metadata instance. * <p> * This registry exposes a single method, {@link #bind}, which can be used * to get the attribute metadata for a particular combination of parent key, * attribute key, and context. * * */ final class AttributeMetadataRegistry { // The root Schema this attribute registry is part of. private final Schema schema; // A map of transforms by context for this attribute. private final Map<TransformKey, AttributeTransform> transforms; // A cache of derived metadata, so we only generate them once. private final ConcurrentMap<TransformKey, AttributeMetadata<?>> cache = new MapMaker().makeMap(); /** * Creates a new attribute registry from the given builder. */ AttributeMetadataRegistry(Schema schema, AttributeMetadataRegistryBuilder builder) { this.schema = schema; this.transforms = getTransforms(builder.getCreators()); } /** * Creates an immutable, deep copy of the given map of attribute builders. * This makes the created attribute builders local to this registry, so we * can treat them as immutable even though they really aren't. */ private Map<TransformKey, AttributeTransform> getTransforms( Map<TransformKey, AttributeCreatorImpl> map) { Builder<TransformKey, AttributeTransform> builder = ImmutableMap.builder(); for (Map.Entry<TransformKey, AttributeCreatorImpl> entry : map.entrySet()) { builder.put(entry.getKey(), entry.getValue().toTransform()); } return builder.build(); } /** * Binds the attribute to the given parent and context. Will return an * undeclared attribute metadata if no metadata for the given key exists. */ <D> AttributeMetadata<D> bind( ElementKey<?, ?> parent, AttributeKey<D> key, MetadataContext context) { Preconditions.checkNotNull(parent, "parent"); Preconditions.checkNotNull(key, "key"); TransformKey transformKey = TransformKey.forTransform(parent, key, context); @SuppressWarnings("unchecked") AttributeMetadata<D> transformed = (AttributeMetadata<D>) cache.get(transformKey); if (transformed == null) { AttributeTransform transform = getTransform(transformKey, key); transformed = transform.toMetadata(schema, parent, key, context); @SuppressWarnings("unchecked") AttributeMetadata<D> previous = (AttributeMetadata<D>) cache.putIfAbsent(transformKey, transformed); if (previous != null) { transformed = previous; } } return transformed; } /** * Provides direct access to the transform for other classes in this package, * to avoid circular dependencies causing infinite loops. This allows * interested classes to access the metadata information for a key without * fully binding it. */ AttributeTransform getTransform(ElementKey<?, ?> parent, AttributeKey<?> key, MetadataContext context) { return getTransform(TransformKey.forTransform(parent, key, context), key); } /** * Gets a composite transform for the given transform key. Will return an * empty transform if no transform matched the given key. */ private AttributeTransform getTransform(TransformKey transformKey, AttributeKey<?> key) { List<AttributeTransform> matched = Lists.newArrayList(); for (Map.Entry<TransformKey, AttributeTransform> entry : transforms.entrySet()) { if (entry.getKey().matches(transformKey)) { matched.add(entry.getValue()); } } // If there were no matches, we use an undeclared attribute, otherwise we // return either a single or composite transform. switch (matched.size()) { case 0: return AttributeTransform.EMPTY; case 1: return matched.get(0); default: return AttributeTransform.create(matched); } } }