/* 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.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 around a single element type. Holds all of the
* transforms and adaptations for that type and for any attributes on the type.
* Child elements are retrieved through the root registry.
*
*
*/
final class ElementMetadataRegistry {
// The root Schema this element registry is part of.
private final Schema schema;
// A map of transform key to the transformed builder at that key.
private final Map<TransformKey, ElementTransform> transforms;
// A cache of derived metadata, so we only generate them once.
private final ConcurrentMap<TransformKey, ElementMetadata<?, ?>> cache
= new MapMaker().makeMap();
/**
* Constructs an element registry for the given builder.
*/
ElementMetadataRegistry(Schema schema,
ElementMetadataRegistryBuilder elementBuilder) {
this.schema = schema;
this.transforms = getTransforms(elementBuilder);
}
/**
* Loops through the transforms on the given builder and any parent builders,
* adding then to our immutable map of transforms.
*/
private Map<TransformKey, ElementTransform> getTransforms(
ElementMetadataRegistryBuilder elementBuilder) {
Builder<TransformKey, ElementTransform> builder = ImmutableMap.builder();
for (Map.Entry<TransformKey, ElementCreatorImpl> entry
: elementBuilder.getCreators().entrySet()) {
builder.put(entry.getKey(), entry.getValue().toTransform());
}
return builder.build();
}
/**
* Binds the metadata when it is inside the given parent and operating
* within the given context.
*/
<D, E extends Element> ElementMetadata<D, E> bind(
ElementKey<?, ?> parent, ElementKey<D, E> key, MetadataContext context) {
TransformKey transformKey = TransformKey.forTransform(parent, key, context);
@SuppressWarnings("unchecked")
ElementMetadata<D, E> transformed =
(ElementMetadata<D, E>) cache.get(transformKey);
if (transformed == null) {
ElementTransform transform = getTransform(transformKey, key);
transformed = transform.toMetadata(schema, parent, key, context);
@SuppressWarnings("unchecked")
ElementMetadata<D, E> previous =
(ElementMetadata<D, E>) 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.
*/
ElementTransform getTransform(ElementKey<?, ?> parent,
ElementKey<?, ?> key, MetadataContext context) {
TransformKey transformKey = TransformKey.forTransform(parent, key, context);
return getTransform(transformKey, key);
}
/**
* Gets a composite transform from all transforms that match the given key.
*/
private ElementTransform getTransform(TransformKey transformKey,
ElementKey<?, ?> key) {
List<ElementTransform> matched = Lists.newArrayList();
for (Map.Entry<TransformKey, ElementTransform> entry
: transforms.entrySet()) {
if (entry.getKey().matches(transformKey)) {
matched.add(entry.getValue());
}
}
switch (matched.size()) {
case 0: return ElementTransform.EMPTY;
case 1: return matched.get(0);
default: return ElementTransform.create(key, matched);
}
}
}