/** * Copyright 2009 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 org.waveprotocol.wave.client.editor; import org.waveprotocol.wave.client.editor.content.ContentElement; import org.waveprotocol.wave.client.editor.content.ContentNode; import org.waveprotocol.wave.client.editor.content.NiceHtmlRenderer; import org.waveprotocol.wave.client.editor.content.Renderer; import org.waveprotocol.wave.model.util.ChainedData; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.DataDomain; import org.waveprotocol.wave.model.util.StringMap; /** * Registry for different types of handlers for document elements, based * on a match condition. Currently just tag name plus special attribute * matching is supported. * * There is a limitation in this approach, namely that handler subclasses * are not recognised - so, if you call getHandler to get a handler, you will * only get a result when passing in the exact .class with which it was * registered. For example, if you subclass Renderer, you'll probably need * to register the renderer twice, both with Renderer.class and YourRenderer.class, * or do type casting on the return value from Renderer.class. * * TODO(danilatos): Conceive of a nicer registering mechanism than class plus * tagname plus attribute * * @author danilatos@google.com (Daniel Danilatos) */ public class ElementHandlerRegistry { /** * The minimum interface required by the registry to identify an element in * order to ascertain look up its handlers. */ public interface HasHandlers { String getTagName(); } private static class HandlerData { /** Handers for defining renderings for elements. */ final StringMap<Renderer> renderers = CollectionUtils.createStringMap(); /** Handlers for the events raised by interacting with an element. */ final StringMap<NodeEventHandler> eventHandlers = CollectionUtils.createStringMap(); /** Handlers for events raised by document tree mutations. */ final StringMap<NodeMutationHandler<ContentNode, ContentElement>> mutationHandlers = CollectionUtils.createStringMap(); /** Handlers for rendering to nice semantic html mode (rather than what looks good). */ // NOTE(patcoleman): ideally should be removed from here, nice renderers could be registered // with the nice html formatter instead (or inferred from implementing a Renderer subtype). final StringMap<NiceHtmlRenderer> niceHtmlRenderers = CollectionUtils.createStringMap(); /** Clears all known handler mappings. */ void clear() { renderers.clear(); eventHandlers.clear(); mutationHandlers.clear(); niceHtmlRenderers.clear(); } } private static final DataDomain<HandlerData, HandlerData> handlerDataDomain = new DataDomain<HandlerData, HandlerData>() { @Override public void compose(HandlerData target, HandlerData changes, HandlerData base) { target.clear(); copyInto(target, base); copyInto(target, changes); } private void copyInto(final HandlerData target, HandlerData source) { target.renderers.putAll(source.renderers); target.eventHandlers.putAll(source.eventHandlers); target.mutationHandlers.putAll(source.mutationHandlers); target.niceHtmlRenderers.putAll(source.niceHtmlRenderers); } @Override public HandlerData empty() { return new HandlerData(); } @Override public HandlerData readOnlyView(HandlerData modifiable) { return modifiable; } }; /** * The singleton registry that, as the terminal of the priority chain, is * always consulted last by any registry. */ public static final ElementHandlerRegistry ROOT = new ElementHandlerRegistry(); private final ChainedData<HandlerData, HandlerData> data; private ElementHandlerRegistry() { data = new ChainedData<HandlerData, HandlerData>(handlerDataDomain); } /** * @param parent parent registry in the chain */ private ElementHandlerRegistry(ElementHandlerRegistry parent) { data = new ChainedData<HandlerData, HandlerData>(parent.data); } /** * @return a chained child */ public ElementHandlerRegistry createExtension() { return new ElementHandlerRegistry(this); } /// Specific handler management /** Register a renderer for a given tag name. */ public void registerRenderer(String tag, Renderer renderer) { data.modify().renderers.put(tag, renderer); } /** Register an event handler for a given tag name. */ public void registerEventHandler(String tag, NodeEventHandler eventHandler) { data.modify().eventHandlers.put(tag, eventHandler); } /** Register a mutation handler for a given tag name. */ public void registerMutationHandler(String tag, NodeMutationHandler<ContentNode, ContentElement> mutationHandler) { data.modify().mutationHandlers.put(tag, mutationHandler); } /** Register both a renderer and a mutation handler for a given tag name. */ public void registerRenderingMutationHandler(String tag, RenderingMutationHandler handler) { HandlerData mutable = data.modify(); mutable.renderers.put(tag, handler); mutable.mutationHandlers.put(tag, handler); } /** Register a pretty semantic html renderer for a tag. */ public void registerNiceHtmlRenderer(String tag, NiceHtmlRenderer renderer) { data.modify().niceHtmlRenderers.put(tag, renderer); } /** Retrieve the renderer instance for an item. */ public Renderer getRenderer(HasHandlers target) { return data.inspect().renderers.get(target.getTagName()); } /** Retrieve the event handler instance for an item. */ public NodeEventHandler getEventHandler(HasHandlers target) { return data.inspect().eventHandlers.get(target.getTagName()); } /** Retrieve the mutation handler instance for an item. */ public NodeMutationHandler<ContentNode, ContentElement> getMutationHandler(HasHandlers target) { return data.inspect().mutationHandlers.get(target.getTagName()); } /** Retrieve the clean html renderer instance for an item. */ public NiceHtmlRenderer getNiceHtmlRenderer(HasHandlers target) { return data.inspect().niceHtmlRenderers.get(target.getTagName()); } }