/* 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.wireformats; import com.google.gdata.util.common.base.Preconditions; import com.google.common.collect.Maps; import com.google.gdata.util.ContentType; import com.google.gdata.wireformats.input.InputParser; import com.google.gdata.wireformats.output.OutputGenerator; import java.util.Collection; import java.util.Collections; import java.util.Map; /** * The AltRegistry class maintains a registry of supported alternate * representation formats along with the configuration of input and output * handlers that will be used to process wire format content using the * representations. * <p> * The registry supports a delegation model where lookup failures can be * delegated to a different registry to support tiered lookup models. It also * has a locking model to avoid changes occurring after initialization has been * completed. * <p> * The class provides a locking model where the contents of the registry can be * made immutable using the {@link #lock()} method. After this call, no * subsequent changes may be made to the registry and it is thread safe to * share across multiple threads. * * */ public class AltRegistry { /** * Simple value helper class containing the configuration of handers for a * registered format. */ private static class AltHandlers { private final InputParser<?> parser; private final OutputGenerator<?> generator; AltHandlers(InputParser<?> parser, OutputGenerator<?> generator) { this.parser = parser; this.generator = generator; } } /** * Contains a mapping from alt parameter name values to the associated format * instance selected by each name. */ private final Map<String, AltFormat> nameMap; /** * Contains a mapping from alt parameter media type names to an alt format * that represents the alternate content representation type * selected by the media type. */ private final Map<String, AltFormat> typeMap; /** * Contains a mapping from an alt format instance to the configuration for the * format */ private final Map<AltFormat, AltHandlers> altHandlers; /** * If {@code true}, indicates that the registry is locked against further * changes. */ private boolean locked; /** * If non-null, contains a default registry that will be used as a delegate * for all lookups that cannot be satisfied by this instance. */ private AltRegistry delegate; /** * Constructs a new AltRegistry with no default registry configuration. */ public AltRegistry() { this(null); } /** * Constructs a new AltRegistry with identical registration state to the * provided registry. If the input value is {@code null}, a clean registry * will be created. * * @param origRegistry registry containing default configuration that will be * used for lookups that can't be satisfied by the local registry. */ public AltRegistry(AltRegistry origRegistry) { if (origRegistry != null) { nameMap = Maps.newHashMap(origRegistry.nameMap); typeMap = Maps.newHashMap(origRegistry.typeMap); altHandlers = Maps.newHashMap(origRegistry.altHandlers); delegate = origRegistry.delegate; } else { nameMap = Maps.newHashMap(); typeMap = Maps.newHashMap(); altHandlers = Maps.newHashMap(); } } /** * Registers the name mapping(s) for an alternate representation. * * @param format format to register name aliases for. */ private void registerFormat(AltFormat format) { nameMap.put(format.getName(), format); if (format.isSelectableByType()) { typeMap.put(format.getContentType().getMediaType(), format); } } /** * Registers the configuration for an {@link AltFormat}, replacing any * existing configuration for the format in the registry. * * @param format format to register * @param parser input parser to use for the format (or {@code null} if not * supported for input. * @param generator output generator to use for the format. * @throws IllegalStateException if registry has been locked. */ public void register(AltFormat format, InputParser<?> parser, OutputGenerator<?> generator) { Preconditions.checkNotNull(format); Preconditions.checkNotNull(generator); Preconditions.checkState(!locked, "Registry is locked against changes"); registerFormat(format); altHandlers.put(format, new AltHandlers(parser, generator)); } /** * Locks the registry against further changes. Any attempts to call the * {@link #register(AltFormat, InputParser, OutputGenerator)} API * after lock has been called will fail. */ public void lock() { locked = true; } /** * Returns the alt format that has been registered with the specified name. * * @param name format name. * @return registered format matching the name or {@code null}. */ public AltFormat lookupName(String name) { AltFormat format = nameMap.get(name); if (format == null && delegate != null) { format = delegate.lookupName(name); } return format; } /** * Returns the alt format that has been registered with the specified content * type. * * @param contentType type to look up. * @return registered format matching the type or {@code null}. */ public AltFormat lookupType(ContentType contentType) { // Do a direct lookup by media type first (fast) AltFormat format = typeMap.get(contentType.getMediaType()); if (format == null) { // If no match, use MIME type matching algorithm (slower but more precise) for (AltFormat testFormat : typeMap.values()) { if (contentType.match(testFormat.getContentType())) { format = testFormat; break; } } } if (format == null && delegate != null) { return delegate.lookupType(contentType); } return format; } /** * Returns a collection of all registered formats in the registry. The * collection does not include any values from the default registry provided * at construction time. * * @return collection of all registered alt formats. */ public Collection<AltFormat> registeredFormats() { return Collections.unmodifiableCollection(altHandlers.keySet()); } /** * Returns the {@link InputParser} for a format or {@code null} if the format * is not registered or does not support input parsing. * * @param altFormat format to locate parser for. * @return parser for format or {@code null}. */ public InputParser<?> getParser(AltFormat altFormat) { AltHandlers handlers = altHandlers.get(altFormat); if (handlers != null) { return handlers.parser; } if (delegate != null) { return delegate.getParser(altFormat); } return null; } /** * Returns the {@link OutputGenerator} for the provided format or {@code null} * if the format is not registered. * * @param altFormat format to locate generator for. * @return generator for format or {@code null}. */ public OutputGenerator<?> getGenerator(AltFormat altFormat) { AltHandlers handlers = altHandlers.get(altFormat); if (handlers != null) { return handlers.generator; } if (delegate != null) { return delegate.getGenerator(altFormat); } return null; } /** * Sets a delegate registry that will be used to satisfy lookups that cannot * be resolved from the local registry. * * @param delegate delegate registry. */ public void setDelegate(AltRegistry delegate) { Preconditions.checkState(!locked, "Registry is locked against changes"); this.delegate = delegate; } /** * Returns {@code true} has an identical registration with the target * registry for the specified alt format. Currently, this is only true if * the copy constructor was used to do the initial registration and no * subsequent changes have been made. * * @param targetRegistry target registry to test * @param altFormat alt format to test * @return {@code true} if registration for format is the same. */ public boolean hasSameHandlers(AltRegistry targetRegistry, AltFormat altFormat) { AltHandlers thisHandlers = altHandlers.get(altFormat); AltHandlers targetHandlers = targetRegistry.altHandlers.get(altFormat); if (thisHandlers == null) { return targetHandlers == null; } return (targetHandlers == null) ? false : thisHandlers.generator == targetHandlers.generator && thisHandlers.parser == targetHandlers.parser; } }