/** * 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.model.wave.data.impl; import org.waveprotocol.wave.model.document.Doc; import org.waveprotocol.wave.model.document.MutableDocument; import org.waveprotocol.wave.model.document.ObservableDocument; import org.waveprotocol.wave.model.document.ObservableMutableDocument; import org.waveprotocol.wave.model.document.indexed.DocumentHandler; import org.waveprotocol.wave.model.document.operation.DocInitialization; import org.waveprotocol.wave.model.document.operation.automaton.DocumentSchema; import org.waveprotocol.wave.model.document.raw.impl.Element; import org.waveprotocol.wave.model.document.raw.impl.Node; import org.waveprotocol.wave.model.document.raw.impl.Text; import org.waveprotocol.wave.model.id.WaveletId; import org.waveprotocol.wave.model.operation.OperationException; import org.waveprotocol.wave.model.schema.SchemaProvider; import org.waveprotocol.wave.model.util.CopyOnWriteSet; import org.waveprotocol.wave.model.wave.data.DocumentFactory; import org.waveprotocol.wave.model.wave.data.ObservableDocumentOperationSink; /** * Extension of a mutable-document wrapper that exposes listener addition and * removal (i.e., makes it observable). * */ public class ObservablePluggableMutableDocument extends PluggableMutableDocument implements ObservableDocumentOperationSink<Doc.N, Doc.E, Doc.T>, ObservableDocument { /** * Factory. */ public static DocumentFactory<ObservablePluggableMutableDocument> createFactory( final SchemaProvider schemas) { return new DocumentFactory<ObservablePluggableMutableDocument>() { @Override public ObservablePluggableMutableDocument create( // \u2620 WaveletId waveletId, String docId, DocInitialization content) { return new ObservablePluggableMutableDocument( schemas.getSchemaForId(waveletId, docId), content); } }; } /** * Fanning broadcast handler */ private final static class DocumentHandlerManager // \u2620 implements DocumentHandler<Node, Element, Text> { private final CopyOnWriteSet<DocumentHandler<Doc.N, Doc.E, Doc.T>> handlers = CopyOnWriteSet.create(); private boolean isPaused = false; // Conversion from Node -> Doc.N, etc. This is safe because Doc<N, E, T> types are // covariant in their node type parameters, it's just this cannot be expressed // at definition time due to a limitation of java, and must be expressed at use // time with wild cards (which then propagate everywhere, causing nastiness). // // Avoiding use of call-site wild cards with an unchecked conversion instead. @SuppressWarnings("unchecked") @Override public void onDocumentEvents(EventBundle event) { if (isPaused) { return; } for (DocumentHandler<Doc.N, Doc.E, Doc.T> handler : handlers) { handler.onDocumentEvents(event); } } void addHandler(DocumentHandler<Doc.N, Doc.E, Doc.T> h) { handlers.add(h); } void removeHandler(DocumentHandler<Doc.N, Doc.E, Doc.T> h) { handlers.remove(h); } private void setPaused(boolean isPaused) { this.isPaused = isPaused; } } /** The direct listener to inject into the (observable) indexed document. */ private final DocumentHandlerManager handlerManager; /** * Creates an observable document. * * @param content initialization content */ public ObservablePluggableMutableDocument(DocumentSchema schema, DocInitialization content) { this(schema, content, new DocumentHandlerManager()); } /** * Creates an observable document * * @param content initialization content * @param handlerManager event broadcaster */ private ObservablePluggableMutableDocument(DocumentSchema schema, DocInitialization content, DocumentHandlerManager handlerManager) { super(content, schema, handlerManager); this.handlerManager = handlerManager; } @Override public void with(final ObservableMutableDocument.Action actionToRunWithDocument) { super.with(new MutableDocument.Action() { public <N, E extends N, T extends N> void exec(MutableDocument<N, E, T> doc) { actionToRunWithDocument.exec(ObservablePluggableMutableDocument.this); }}); } @Override public <V> V with(final ObservableMutableDocument.Method<V> methodToRunWithDocument) { return super.with(new MutableDocument.Method<V>() { public <N, E extends N, T extends N> V exec(MutableDocument<N, E, T> doc) { return methodToRunWithDocument.exec(ObservablePluggableMutableDocument.this); } }); } @Override public void addListener(DocumentHandler<Doc.N, Doc.E, Doc.T> listener) { handlerManager.addHandler(listener); } @Override public void removeListener(DocumentHandler<Doc.N, Doc.E, Doc.T> listener) { handlerManager.removeHandler(listener); } @Override protected void createSubstrateDocument() throws OperationException { handlerManager.setPaused(true); super.createSubstrateDocument(); handlerManager.setPaused(false); } }