/** * Copyright 2010 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.wavepanel.render; import org.waveprotocol.wave.client.editor.Editor; import org.waveprotocol.wave.client.editor.content.Registries; import org.waveprotocol.wave.client.doodad.DoodadInstallers.BlipInstaller; import org.waveprotocol.wave.client.doodad.DoodadInstallers.ConversationInstaller; import org.waveprotocol.wave.client.doodad.DoodadInstallers.GlobalInstaller; import org.waveprotocol.wave.model.conversation.Conversation; import org.waveprotocol.wave.model.conversation.ConversationBlip; import org.waveprotocol.wave.model.conversation.WaveletBasedConversation; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.IdentityMap; import org.waveprotocol.wave.model.wave.Wavelet; import java.util.Collection; import java.util.Collections; /** * A collection of doodad handlers, used to create {@link Registries} for * documents on demand. * * Doodad handlers are categorised into global, per-conversation, and per-blip, * according to the needs of each particular doodad handler. * */ public final class DocumentRegistries { /** * Builder that collects doodads to be installed. After collection, the set of * handlers is concretized into a {@link DocumentRegistries}, and handlers can * not be installed after that. */ public static final class Builder { private Collection<GlobalInstaller> gis; private Collection<ConversationInstaller> cis; private Collection<BlipInstaller> bis; public Builder use(ConversationInstaller c) { (cis = lazyCreate(cis)).add(c); return this; } public Builder use(BlipInstaller b) { (bis = lazyCreate(bis)).add(b); return this; } public Builder use(GlobalInstaller g) { (gis = lazyCreate(gis)).add(g); return this; } public DocumentRegistries build() { Registries root = Editor.ROOT_REGISTRIES; for (GlobalInstaller gi : nonNull(gis)) { gi.install(root); } return new DocumentRegistries(root, nonNull(cis), nonNull(bis)); } private static <T> Collection<T> lazyCreate(Collection<T> xs) { return xs == null ? CollectionUtils.<T> createQueue() : xs; } private static <T> Collection<T> nonNull(Collection<T> xs) { return xs == null ? Collections.<T> emptyList() : xs; } } /** * Per-conversation registry bundle. */ private class ConversationRegistries { /** Registries from which blip registries are forked. */ // Intentionally make 'registries' refer to the per-conv one. @SuppressWarnings("hiding") private final Registries registries; private final IdentityMap<ConversationBlip, Registries> blipRegistries = CollectionUtils.createIdentityMap(); // Context used to install per-blip handlers. private final Wavelet w; private final Conversation c; ConversationRegistries(Registries registries, Wavelet w, Conversation c) { this.registries = registries; this.w = w; this.c = c; } /** * Extends this conversation's registres for a blip. Per-blip doodads are * installed in the new registries. */ private Registries extendFor(ConversationBlip b) { Registries r = registries.createExtension(); for (BlipInstaller bi : bis) { bi.install(w, c, b, r); } return r; } /** * @return the registries for a blip, lazily creating one if none exists. */ Registries getBlipRegistries(ConversationBlip b) { Registries rs = blipRegistries.get(b); if (rs == null) { rs = extendFor(b); blipRegistries.put(b, rs); } return rs; } } /** Root registries, from which conversation registries are forked. */ private final Registries registries; /** Per-conversation doodads to install in each per-conversation registry. */ private final Collection<ConversationInstaller> cis; /** Per-blip doodads to install in each per-blip registry. */ private final Collection<BlipInstaller> bis; /** * Per-conversation registries (which each contain their own per-blip * registries. */ private final IdentityMap<Conversation, ConversationRegistries> conversationRegistries = CollectionUtils.createIdentityMap(); private DocumentRegistries(Registries r, Collection<ConversationInstaller> cis, Collection<BlipInstaller> bis) { this.registries = r; this.cis = cis; this.bis = bis; } /** @return a builder for the registries collection. */ public static Builder builder() { return new Builder(); } /** Creates a per-conversation registries bundle. */ private ConversationRegistries extendFor(Conversation c) { Registries r = registries.createExtension(); for (ConversationInstaller ci : cis) { ci.install(((WaveletBasedConversation) c).getWavelet(), c, r); } return new ConversationRegistries(r, ((WaveletBasedConversation) c).getWavelet(), c); } /** * Loads the registries bundle for a conversation. If no bundle currently * exists, a new one is created, and the per-conversation doodads are * installed in it. Never returns null. */ private ConversationRegistries getConversationRegistries(Conversation c) { ConversationRegistries rs = conversationRegistries.get(c); if (rs == null) { rs = extendFor(c); conversationRegistries.put(c, rs); } return rs; } /** * Loads the registries bundle for a blip. If no bundle currently exists, a * new one is created, and the per-blip doodads are installed in it. Never * returns null. */ public Registries get(ConversationBlip blip) { return getConversationRegistries(blip.getConversation()).getBlipRegistries(blip); } }