http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/StageTwo.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/StageTwo.java b/wave/src/main/java/org/waveprotocol/wave/client/StageTwo.java deleted file mode 100644 index 3efce9e..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/StageTwo.java +++ /dev/null @@ -1,760 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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; - -import com.google.common.base.Preconditions; -import com.google.gwt.dom.client.Element; -import com.google.gwt.user.client.Command; - -import org.waveprotocol.wave.client.account.ProfileManager; -import org.waveprotocol.wave.client.account.impl.ProfileManagerImpl; -import org.waveprotocol.wave.client.common.util.AsyncHolder; -import org.waveprotocol.wave.client.common.util.ClientPercentEncoderDecoder; -import org.waveprotocol.wave.client.common.util.CountdownLatch; -import org.waveprotocol.wave.client.common.util.DateUtils; -import org.waveprotocol.wave.client.concurrencycontrol.LiveChannelBinder; -import org.waveprotocol.wave.client.concurrencycontrol.MuxConnector; -import org.waveprotocol.wave.client.concurrencycontrol.WaveletOperationalizer; -import org.waveprotocol.wave.client.doodad.DoodadInstallers; -import org.waveprotocol.wave.client.doodad.attachment.AttachmentManagerProvider; -import org.waveprotocol.wave.client.doodad.attachment.ImageThumbnail; -import org.waveprotocol.wave.client.doodad.attachment.render.ImageThumbnailWrapper; -import org.waveprotocol.wave.client.doodad.diff.DiffAnnotationHandler; -import org.waveprotocol.wave.client.doodad.diff.DiffDeleteRenderer; -import org.waveprotocol.wave.client.doodad.link.LinkAnnotationHandler; -import org.waveprotocol.wave.client.doodad.link.LinkAnnotationHandler.LinkAttributeAugmenter; -import org.waveprotocol.wave.client.doodad.selection.SelectionAnnotationHandler; -import org.waveprotocol.wave.client.doodad.title.TitleAnnotationHandler; -import org.waveprotocol.wave.client.editor.content.Registries; -import org.waveprotocol.wave.client.editor.content.misc.StyleAnnotationHandler; -import org.waveprotocol.wave.client.gadget.Gadget; -import org.waveprotocol.wave.client.render.ReductionBasedRenderer; -import org.waveprotocol.wave.client.render.RenderingRules; -import org.waveprotocol.wave.client.scheduler.Scheduler.Task; -import org.waveprotocol.wave.client.scheduler.SchedulerInstance; -import org.waveprotocol.wave.client.state.BlipReadStateMonitor; -import org.waveprotocol.wave.client.state.BlipReadStateMonitorImpl; -import org.waveprotocol.wave.client.state.ThreadReadStateMonitor; -import org.waveprotocol.wave.client.state.ThreadReadStateMonitorImpl; -import org.waveprotocol.wave.client.uibuilder.UiBuilder; -import org.waveprotocol.wave.client.util.ClientFlags; -import org.waveprotocol.wave.client.wave.InteractiveDocument; -import org.waveprotocol.wave.client.wave.LazyContentDocument; -import org.waveprotocol.wave.client.wave.LocalSupplementedWave; -import org.waveprotocol.wave.client.wave.LocalSupplementedWaveImpl; -import org.waveprotocol.wave.client.wave.RegistriesHolder; -import org.waveprotocol.wave.client.wave.SimpleDiffDoc; -import org.waveprotocol.wave.client.wave.WaveDocuments; -import org.waveprotocol.wave.client.wavepanel.impl.diff.DiffController; -import org.waveprotocol.wave.client.wavepanel.impl.reader.Reader; -import org.waveprotocol.wave.client.wavepanel.render.BlipPager; -import org.waveprotocol.wave.client.wavepanel.render.DocumentRegistries; -import org.waveprotocol.wave.client.wavepanel.render.FullDomRenderer; -import org.waveprotocol.wave.client.wavepanel.render.FullDomRenderer.DocRefRenderer; -import org.waveprotocol.wave.client.wavepanel.render.HtmlDomRenderer; -import org.waveprotocol.wave.client.wavepanel.render.InlineAnchorLiveRenderer; -import org.waveprotocol.wave.client.wavepanel.render.LiveConversationViewRenderer; -import org.waveprotocol.wave.client.wavepanel.render.PagingHandlerProxy; -import org.waveprotocol.wave.client.wavepanel.render.ReplyManager; -import org.waveprotocol.wave.client.wavepanel.render.ShallowBlipRenderer; -import org.waveprotocol.wave.client.wavepanel.render.UndercurrentShallowBlipRenderer; -import org.waveprotocol.wave.client.wavepanel.view.ModelIdMapper; -import org.waveprotocol.wave.client.wavepanel.view.ModelIdMapperImpl; -import org.waveprotocol.wave.client.wavepanel.view.ViewIdMapper; -import org.waveprotocol.wave.client.wavepanel.view.dom.DomAsViewProvider; -import org.waveprotocol.wave.client.wavepanel.view.dom.ModelAsViewProvider; -import org.waveprotocol.wave.client.wavepanel.view.dom.ModelAsViewProviderImpl; -import org.waveprotocol.wave.client.wavepanel.view.dom.full.BlipQueueRenderer; -import org.waveprotocol.wave.client.wavepanel.view.dom.full.DomRenderer; -import org.waveprotocol.wave.client.wavepanel.view.dom.full.ViewFactories; -import org.waveprotocol.wave.client.wavepanel.view.dom.full.ViewFactory; -import org.waveprotocol.wave.client.wavepanel.view.dom.full.WavePanelResourceLoader; -import org.waveprotocol.wave.common.logging.LoggerBundle; -import org.waveprotocol.wave.concurrencycontrol.channel.OperationChannelMultiplexer; -import org.waveprotocol.wave.concurrencycontrol.channel.OperationChannelMultiplexerImpl; -import org.waveprotocol.wave.concurrencycontrol.channel.OperationChannelMultiplexerImpl.LoggerContext; -import org.waveprotocol.wave.concurrencycontrol.channel.ViewChannelFactory; -import org.waveprotocol.wave.concurrencycontrol.channel.ViewChannelImpl; -import org.waveprotocol.wave.concurrencycontrol.channel.WaveViewService; -import org.waveprotocol.wave.concurrencycontrol.common.UnsavedDataListener; -import org.waveprotocol.wave.concurrencycontrol.common.UnsavedDataListenerFactory; -import org.waveprotocol.wave.model.conversation.ConversationBlip; -import org.waveprotocol.wave.model.conversation.ConversationThread; -import org.waveprotocol.wave.model.conversation.ObservableConversationView; -import org.waveprotocol.wave.model.conversation.WaveBasedConversationView; -import org.waveprotocol.wave.model.document.indexed.IndexedDocumentImpl; -import org.waveprotocol.wave.model.document.operation.DocInitialization; -import org.waveprotocol.wave.model.id.IdConstants; -import org.waveprotocol.wave.model.id.IdFilter; -import org.waveprotocol.wave.model.id.IdGenerator; -import org.waveprotocol.wave.model.id.IdGeneratorImpl; -import org.waveprotocol.wave.model.id.IdGeneratorImpl.Seed; -import org.waveprotocol.wave.model.id.IdURIEncoderDecoder; -import org.waveprotocol.wave.model.id.WaveId; -import org.waveprotocol.wave.model.id.WaveletId; -import org.waveprotocol.wave.model.schema.SchemaProvider; -import org.waveprotocol.wave.model.supplement.LiveSupplementedWaveImpl; -import org.waveprotocol.wave.model.supplement.ObservablePrimitiveSupplement; -import org.waveprotocol.wave.model.supplement.ObservableSupplementedWave; -import org.waveprotocol.wave.model.supplement.SupplementedWaveImpl.DefaultFollow; -import org.waveprotocol.wave.model.supplement.WaveletBasedSupplement; -import org.waveprotocol.wave.model.util.FuzzingBackOffScheduler; -import org.waveprotocol.wave.model.util.FuzzingBackOffScheduler.CollectiveScheduler; -import org.waveprotocol.wave.model.util.IdentityMap; -import org.waveprotocol.wave.model.util.Scheduler; -import org.waveprotocol.wave.model.version.HashedVersion; -import org.waveprotocol.wave.model.version.HashedVersionFactory; -import org.waveprotocol.wave.model.version.HashedVersionZeroFactoryImpl; -import org.waveprotocol.wave.model.wave.ParticipantId; -import org.waveprotocol.wave.model.wave.Wavelet; -import org.waveprotocol.wave.model.wave.data.DocumentFactory; -import org.waveprotocol.wave.model.wave.data.ObservableWaveletData; -import org.waveprotocol.wave.model.wave.data.WaveViewData; -import org.waveprotocol.wave.model.wave.data.impl.ObservablePluggableMutableDocument; -import org.waveprotocol.wave.model.wave.data.impl.WaveletDataImpl; -import org.waveprotocol.wave.model.wave.opbased.ObservableWaveView; -import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet; -import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl; -import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl.WaveletConfigurator; -import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl.WaveletFactory; - -import java.util.Collections; -import java.util.Map; - -/** - * The second stage of client code. - * <p> - * This stage builds the wave model in memory, and also opens the channel to - * make it live. Rendering code that operates on the model is also established - * in this stage. - * - */ -public interface StageTwo { - - /** @return the (live) conversations in the wave. */ - ObservableConversationView getConversations(); - - /** @return the core wave. */ - ObservableWaveView getWave(); - - /** @return the signed-in user's (live) supplementary data in the wave. */ - LocalSupplementedWave getSupplement(); - - /** @return live blip read/unread information. */ - BlipReadStateMonitor getReadMonitor(); - - /** @return the registry of document objects used for conversational blips. */ - WaveDocuments<? extends InteractiveDocument> getDocumentRegistry(); - - /** @return the provider of view objects given model objects. */ - ModelAsViewProvider getModelAsViewProvider(); - - /** @return the profile manager. */ - ProfileManager getProfileManager(); - - /** @return the id generator. */ - IdGenerator getIdGenerator(); - - /** @return the communication channel connector. */ - MuxConnector getConnector(); - - /** - * @return the blip content renderer, which needs to be flushed by anything - * requiring synchronous rendering. - */ - BlipQueueRenderer getBlipQueue(); - - /** @return controller of diff state. */ - DiffController getDiffController(); - - /** @return the signed in user. */ - ParticipantId getSignedInUser(); - - /** @return a unique string identifying this session. */ - String getSessionId(); - - /** @return stage one. */ - StageOne getStageOne(); - - /** @return Reader. */ - Reader getReader(); - - /** - * Default implementation of the stage two configuration. Each component is - * defined by a factory method, any of which may be overridden in order to - * stub out some dependencies. Circular dependencies are not detected. - * - */ - public static abstract class DefaultProvider extends AsyncHolder.Impl<StageTwo> - implements StageTwo { - // Asynchronously constructed and external dependencies - protected final StageOne stageOne; - private WaveViewData waveData; - - // - // Synchronously constructed dependencies. - // - - // Client stuff. - - private String sessionId; - private ParticipantId signedInuser; - private CollectiveScheduler rpcScheduler; - - // Wave stack. - - private IdGenerator idGenerator; - private WaveDocuments<LazyContentDocument> documentRegistry; - private WaveletOperationalizer wavelets; - private WaveViewImpl<OpBasedWavelet> wave; - private MuxConnector connector; - - // Model objects - - private ProfileManager profileManager; - private ObservableConversationView conversations; - private LocalSupplementedWave supplement; - private BlipReadStateMonitor readMonitor; - - // State Monitors - private ThreadReadStateMonitor threadReadStateMonitor; - - // Rendering objects. - - private ViewIdMapper viewIdMapper; - private ShallowBlipRenderer blipDetailer; - private DomRenderer renderer; - private BlipQueueRenderer queueRenderer; - private ModelAsViewProvider modelAsView; - private DiffController diffController; - private Reader reader; - - private final UnsavedDataListener unsavedDataListener; - - public DefaultProvider(StageOne stageOne, UnsavedDataListener unsavedDataListener) { - this.stageOne = stageOne; - this.unsavedDataListener = unsavedDataListener; - } - - /** - * Creates the second stage. - */ - @Override - protected void create(final Accessor<StageTwo> whenReady) { - onStageInit(); - - final CountdownLatch synchronizer = CountdownLatch.create(2, new Command() { - @Override - public void execute() { - install(); - onStageLoaded(); - whenReady.use(DefaultProvider.this); - } - }); - - fetchWave(new Accessor<WaveViewData>() { - @Override - public void use(WaveViewData x) { - waveData = x; - synchronizer.tick(); - } - }); - - // Defer everything else, to let the RPC go out. - SchedulerInstance.getMediumPriorityTimer().scheduleDelayed(new Task() { - @Override - public void execute() { - installStatics(); - synchronizer.tick(); - } - }, 20); - } - - /** Notifies this provider that the stage is about to be loaded. */ - protected void onStageInit() { - } - - /** Notifies this provider that the stage has been loaded. */ - protected void onStageLoaded() { - } - - @Override - public final StageOne getStageOne() { - return stageOne; - } - - @Override - public final String getSessionId() { - return sessionId == null ? sessionId = createSessionId() : sessionId; - } - - protected final ViewIdMapper getViewIdMapper() { - return viewIdMapper == null ? viewIdMapper = createViewIdMapper() : viewIdMapper; - } - - protected final ShallowBlipRenderer getBlipDetailer() { - return blipDetailer == null ? blipDetailer = createBlipDetailer() : blipDetailer; - } - - protected final DomRenderer getRenderer() { - return renderer == null ? renderer = createRenderer() : renderer; - } - - protected final ThreadReadStateMonitor getThreadReadStateMonitor() { - return threadReadStateMonitor == null ? threadReadStateMonitor = - createThreadReadStateMonitor() : threadReadStateMonitor; - } - - @Override - public final BlipQueueRenderer getBlipQueue() { - return queueRenderer == null ? queueRenderer = createBlipQueueRenderer() : queueRenderer; - } - - @Override - public final ModelAsViewProvider getModelAsViewProvider() { - return modelAsView == null ? modelAsView = createModelAsViewProvider() : modelAsView; - } - - @Override - public final ParticipantId getSignedInUser() { - return signedInuser == null ? signedInuser = createSignedInUser() : signedInuser; - } - - @Override - public final IdGenerator getIdGenerator() { - return idGenerator == null ? idGenerator = createIdGenerator() : idGenerator; - } - - /** @return the scheduler to use for RPCs. */ - protected final CollectiveScheduler getRpcScheduler() { - return rpcScheduler == null ? rpcScheduler = createRpcScheduler() : rpcScheduler; - } - - @Override - public final ProfileManager getProfileManager() { - return profileManager == null ? profileManager = createProfileManager() : profileManager; - } - - @Override - public final MuxConnector getConnector() { - return connector == null ? connector = createConnector() : connector; - } - - @Override - public final WaveViewImpl<OpBasedWavelet> getWave() { - return wave == null ? wave = createWave() : wave; - } - - protected final WaveletOperationalizer getWavelets() { - return wavelets == null ? wavelets = createWavelets() : wavelets; - } - - @Override - public final ObservableConversationView getConversations() { - return conversations == null ? conversations = createConversations() : conversations; - } - - @Override - public final LocalSupplementedWave getSupplement() { - return supplement == null ? supplement = createSupplement() : supplement; - } - - @Override - public final BlipReadStateMonitor getReadMonitor() { - return readMonitor == null ? readMonitor = createReadMonitor() : readMonitor; - } - - @Override - public final WaveDocuments<LazyContentDocument> getDocumentRegistry() { - return documentRegistry == null - ? documentRegistry = createDocumentRegistry() : documentRegistry; - } - - protected final WaveViewData getWaveData() { - Preconditions.checkState(waveData != null, "wave not ready"); - return waveData; - } - - @Override - public final DiffController getDiffController() { - return diffController == null ? diffController = createDiffController() : diffController; - } - - @Override - public final Reader getReader() { - return reader; - } - - /** @return the id mangler for model objects. Subclasses may override. */ - protected ModelIdMapper createModelIdMapper() { - return ModelIdMapperImpl.create(getConversations(), "UC"); - } - - /** @return the id mangler for view objects. Subclasses may override. */ - protected ViewIdMapper createViewIdMapper() { - return new ViewIdMapper(createModelIdMapper()); - } - - /** @return the id of the signed-in user. Subclassses may override. */ - protected abstract ParticipantId createSignedInUser(); - - /** @return the unique id for this client session. */ - protected abstract String createSessionId(); - - /** @return the id generator for model object. Subclasses may override. */ - protected IdGenerator createIdGenerator() { - final String seed = getSessionId(); - // Replace with session. - return new IdGeneratorImpl(getSignedInUser().getDomain(), new Seed() { - @Override - public String get() { - return seed; - } - }); - } - - /** @return the scheduler to use for RPCs. Subclasses may override. */ - protected CollectiveScheduler createRpcScheduler() { - // Use a scheduler that runs closely-timed tasks at the same time. - return new OptimalGroupingScheduler(SchedulerInstance.getLowPriorityTimer()); - } - - protected WaveletOperationalizer createWavelets() { - return WaveletOperationalizer.create(getWaveData().getWaveId(), getSignedInUser()); - } - - protected WaveViewImpl<OpBasedWavelet> createWave() { - WaveViewData snapshot = getWaveData(); - // The operationalizer makes the wavelets function via operation control. - // The hookup with concurrency-control and remote operation streams occurs - // later in createUpgrader(). - final WaveletOperationalizer operationalizer = getWavelets(); - WaveletFactory<OpBasedWavelet> waveletFactory = new WaveletFactory<OpBasedWavelet>() { - @Override - public OpBasedWavelet create(WaveId waveId, WaveletId id, ParticipantId creator) { - long now = System.currentTimeMillis(); - ObservableWaveletData data = new WaveletDataImpl(id, - creator, - now, - 0L, - HashedVersion.unsigned(0), - now, - waveId, - getDocumentRegistry()); - return operationalizer.operationalize(data); - } - }; - WaveViewImpl<OpBasedWavelet> wave = - WaveViewImpl.create(waveletFactory, snapshot.getWaveId(), getIdGenerator(), - getSignedInUser(), WaveletConfigurator.ADD_CREATOR); - - // Populate the initial state. - for (ObservableWaveletData waveletData : snapshot.getWavelets()) { - wave.addWavelet(operationalizer.operationalize(waveletData)); - } - return wave; - } - - /** @return the conversations in the wave. Subclasses may override. */ - protected ObservableConversationView createConversations() { - return WaveBasedConversationView.create(getWave(), getIdGenerator()); - } - - /** @return the user supplement of the wave. Subclasses may override. */ - protected LocalSupplementedWave createSupplement() { - Wavelet udw = getWave().getUserData(); - if (udw == null) { - udw = getWave().createUserData(); - } - ObservablePrimitiveSupplement state = WaveletBasedSupplement.create(udw); - ObservableSupplementedWave live = new LiveSupplementedWaveImpl( - state, getWave(), getSignedInUser(), DefaultFollow.ALWAYS, getConversations()); - return LocalSupplementedWaveImpl.create(getWave(), live); - } - - /** @return a supplement to the supplement, to get exact read/unread counts. */ - protected BlipReadStateMonitor createReadMonitor() { - return BlipReadStateMonitorImpl.create( - getWave().getWaveId(), getSupplement(), getConversations()); - } - - /** @return the registry of documents in the wave. Subclasses may override. */ - protected WaveDocuments<LazyContentDocument> createDocumentRegistry() { - IndexedDocumentImpl.performValidation = false; - - DocumentFactory<?> dataDocFactory = - ObservablePluggableMutableDocument.createFactory(createSchemas()); - DocumentFactory<LazyContentDocument> blipDocFactory = - new DocumentFactory<LazyContentDocument>() { - private final Registries registries = RegistriesHolder.get(); - - @Override - public LazyContentDocument create( - WaveletId waveletId, String docId, DocInitialization content) { - // TODO(piotrkaleta,hearnden): hook up real diff state. - SimpleDiffDoc noDiff = SimpleDiffDoc.create(content, null); - return LazyContentDocument.create(registries, noDiff); - } - }; - - return WaveDocuments.create(blipDocFactory, dataDocFactory); - } - - protected abstract SchemaProvider createSchemas(); - - /** @return the RPC interface for wave communication. */ - protected abstract WaveViewService createWaveViewService(); - - /** @return upgrader for activating stacklets. Subclasses may override. */ - protected MuxConnector createConnector() { - LoggerBundle logger = LoggerBundle.NOP_IMPL; - LoggerContext loggers = new LoggerContext(logger, logger, logger, logger); - - IdURIEncoderDecoder uriCodec = new IdURIEncoderDecoder(new ClientPercentEncoderDecoder()); - HashedVersionFactory hashFactory = new HashedVersionZeroFactoryImpl(uriCodec); - - Scheduler scheduler = new FuzzingBackOffScheduler.Builder(getRpcScheduler()) - .setInitialBackOffMs(ClientFlags.get().initialRpcBackoffMs()) - .setMaxBackOffMs(ClientFlags.get().maxRpcBackoffMs()) - .setRandomisationFactor(0.5) - .build(); - - ViewChannelFactory viewFactory = ViewChannelImpl.factory(createWaveViewService(), logger); - UnsavedDataListenerFactory unsyncedListeners = new UnsavedDataListenerFactory() { - - private final UnsavedDataListener listener = unsavedDataListener; - - @Override - public UnsavedDataListener create(WaveletId waveletId) { - return listener; - } - - @Override - public void destroy(WaveletId waveletId) { - } - }; - - WaveletId udwId = getIdGenerator().newUserDataWaveletId(getSignedInUser().getAddress()); - final IdFilter filter = IdFilter.of(Collections.singleton(udwId), - Collections.singleton(IdConstants.CONVERSATION_WAVELET_PREFIX)); - - WaveletDataImpl.Factory snapshotFactory = - WaveletDataImpl.Factory.create(getDocumentRegistry()); - final OperationChannelMultiplexer mux = - new OperationChannelMultiplexerImpl(getWave().getWaveId(), - viewFactory, - snapshotFactory, - loggers, - unsyncedListeners, - scheduler, - hashFactory); - - final WaveViewImpl<OpBasedWavelet> wave = getWave(); - - return new MuxConnector() { - @Override - public void connect(Command onOpened) { - LiveChannelBinder.openAndBind(getWavelets(), - wave, - getDocumentRegistry(), - mux, - filter, - onOpened); - } - - @Override - public void close() { - mux.close(); - } - }; - } - - /** @return the manager of user identities. Subclasses may override. */ - protected ProfileManager createProfileManager() { - return new ProfileManagerImpl(); - } - - /** @return the renderer of intrinsic blip state. Subclasses may override. */ - protected ShallowBlipRenderer createBlipDetailer() { - return new UndercurrentShallowBlipRenderer(getProfileManager(), getSupplement(), DateUtils.getInstance()); - } - - /** @return the thread state monitor. Subclasses may override. */ - protected ThreadReadStateMonitor createThreadReadStateMonitor() { - return ThreadReadStateMonitorImpl.create(getSupplement(), getConversations()); - } - - /** @return the renderer of intrinsic blip state. Subclasses may override. */ - protected BlipQueueRenderer createBlipQueueRenderer() { - DomAsViewProvider domAsView = stageOne.getDomAsViewProvider(); - ReplyManager replyManager = new ReplyManager(getModelAsViewProvider()); - - // Add all doodads here. - DocumentRegistries doodads = installDoodads(DocumentRegistries.builder()) // \u2620 - .use(InlineAnchorLiveRenderer.installer(getViewIdMapper(), replyManager, domAsView)) - .use(Gadget.install(getProfileManager(), getSupplement(), getSignedInUser())) - .build(); - - LiveConversationViewRenderer live = - LiveConversationViewRenderer.create(SchedulerInstance.getLowPriorityTimer(), - getConversations(), getModelAsViewProvider(), getBlipDetailer(), replyManager, - getThreadReadStateMonitor(), getProfileManager(), getSupplement()); - live.init(); - - BlipPager pager = BlipPager.create( - getDocumentRegistry(), doodads, domAsView, getModelAsViewProvider(), getBlipDetailer(), - stageOne.getWavePanel().getGwtPanel()); - - // Collect various components required for paging blips in/out. - PagingHandlerProxy pagingHandler = PagingHandlerProxy.create( // \u2620 - // Enables and disables the document rendering, as well blip metadata. - pager, - // Registers and deregisters profile listeners for name changes. - live); - - return BlipQueueRenderer.create(pagingHandler); - } - - protected ViewFactory createViewFactories() { - return ViewFactories.FIXED; - } - - protected DomRenderer createRenderer() { - final BlipQueueRenderer pager = getBlipQueue(); - DocRefRenderer docRenderer = new DocRefRenderer() { - @Override - public UiBuilder render( - ConversationBlip blip, IdentityMap<ConversationThread, UiBuilder> replies) { - // Documents are rendered blank, and filled in later when - // they get paged in. - pager.add(blip); - return DocRefRenderer.EMPTY.render(blip, replies); - } - }; - - RenderingRules<UiBuilder> rules = new FullDomRenderer( - getBlipDetailer(), docRenderer, getProfileManager(), - getViewIdMapper(), createViewFactories(), getThreadReadStateMonitor()); - return new HtmlDomRenderer(ReductionBasedRenderer.of(rules, getConversations())); - } - - protected DiffController createDiffController() { - return DiffController.create( - getConversations(), getSupplement(), getDocumentRegistry(), getModelAsViewProvider()); - } - - /** - * Fetches and builds the core wave state. - * - * @param whenReady command to execute when the wave is built - */ - protected abstract void fetchWave(final Accessor<WaveViewData> whenReady); - - /** - * Installs parts of stage two that have no dependencies. - * <p> - * Subclasses may override this to change the set of installed features. - */ - protected void installStatics() { - WavePanelResourceLoader.loadCss(); - } - - protected DocumentRegistries.Builder installDoodads(DocumentRegistries.Builder doodads) { - return doodads.use(new DoodadInstallers.GlobalInstaller() { - @Override - public void install(Registries r) { - DiffAnnotationHandler.register(r.getAnnotationHandlerRegistry(), r.getPaintRegistry()); - DiffDeleteRenderer.register(r.getElementHandlerRegistry()); - StyleAnnotationHandler.register(r); - TitleAnnotationHandler.register(r); - LinkAnnotationHandler.register(r, createLinkAttributeAugmenter()); - SelectionAnnotationHandler.register(r, getSessionId(), getProfileManager()); - ImageThumbnail.register(r.getElementHandlerRegistry(), AttachmentManagerProvider.get(), - new ImageThumbnail.ThumbnailActionHandler() { - - @Override - public boolean onClick(ImageThumbnailWrapper thumbnail) { - return false; - } - }); - } - }); - } - - protected LinkAttributeAugmenter createLinkAttributeAugmenter() { - return new LinkAttributeAugmenter() { - @Override - public Map<String, String> augment(Map<String, Object> annotations, boolean isEditing, - Map<String, String> current) { - return current; - } - }; - } - - protected ModelAsViewProvider createModelAsViewProvider() { - return new ModelAsViewProviderImpl(getViewIdMapper(), stageOne.getDomAsViewProvider()); - } - - /** - * Installs parts of stage two that have dependencies. - * <p> - * This method is only called once all asynchronously loaded components of - * stage two are ready. - * <p> - * Subclasses may override this to change the set of installed features. - */ - protected void install() { - // Install diff control before rendering, because logical diff state may - // need to be adjusted due to arbitrary UI policies. - getDiffController().install(); - - // Install rendering capabilities, then render if necessary. - stageOne.getDomAsViewProvider().setRenderer(getRenderer()); - ensureRendered(); - - // Install eager UI features - installFeatures(); - - // Activate liveness. - getConnector().connect(null); - } - - /** - * Ensures that the wave is rendered. - * <p> - * Subclasses may override (e.g., to use server-side rendering). - */ - protected void ensureRendered() { - // Default behaviour is to render the whole wave. - Element e = getRenderer().render(getConversations()); - stageOne.getWavePanel().init(e); - } - - /** - * Installs the eager features of this stage. - */ - protected void installFeatures() { - // Eagerly install some features. - reader = Reader.install(getSupplement(), stageOne.getFocusFrame(), getModelAsViewProvider(), - getDocumentRegistry()); - } - } -}
http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/StageZero.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/StageZero.java b/wave/src/main/java/org/waveprotocol/wave/client/StageZero.java deleted file mode 100644 index 62f0ecf..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/StageZero.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.core.client.GWT.UncaughtExceptionHandler; - -import org.waveprotocol.wave.client.common.util.AsyncHolder; -import org.waveprotocol.wave.client.common.util.JsoCollectionFactory; -import org.waveprotocol.wave.model.util.CollectionUtils; - -/** - * Encapsulates the entry level module for Undercurrent. - * - * This zeroeth stage is only intended to load critical code that has an - * important reason to be loaded first (e.g., exception handling). - * - */ -public interface StageZero { - - /** - * Default implementation of the zeroeth stage. - */ - public class DefaultProvider extends AsyncHolder.Impl<StageZero> implements StageZero { - - @Override - protected final void create(Accessor<StageZero> whenReady) { - onStageInit(); - // TODO: enable webdriver hook. - GWT.setUncaughtExceptionHandler(createUncaughtExceptionHandler()); - if (GWT.isScript()) { - CollectionUtils.setDefaultCollectionFactory(new JsoCollectionFactory()); - } - onStageLoaded(); - whenReady.use(this); - } - - /** Notifies this provider that the stage is about to be loaded. */ - protected void onStageInit() { - } - - /** Notifies this provider that the stage has been loaded. */ - protected void onStageLoaded() { - } - - /** @return the uncaught exception handler to install. */ - protected UncaughtExceptionHandler createUncaughtExceptionHandler() { - // Use GWT's one by default. - return GWT.getUncaughtExceptionHandler(); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/Stages.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/Stages.java b/wave/src/main/java/org/waveprotocol/wave/client/Stages.java deleted file mode 100644 index f0cab51..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/Stages.java +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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; - -import com.google.gwt.core.client.RunAsyncCallback; -import com.google.gwt.user.client.Command; -import org.waveprotocol.box.stat.Timer; -import org.waveprotocol.box.stat.Timing; - -import org.waveprotocol.wave.client.common.util.AsyncHolder; -import org.waveprotocol.wave.client.common.util.AsyncHolder.Accessor; -import org.waveprotocol.wave.client.scheduler.Scheduler; -import org.waveprotocol.wave.client.scheduler.SchedulerInstance; - -/** - * Loads Undercurrent's stages in order. - * - */ -public abstract class Stages { - - /** Continuation for when the last stage is loaded. */ - private Command whenFinished; - - /** - * Creates a new stage loading sequence. - */ - public Stages() { - } - - /** @return the provider that loads stage zero. */ - protected AsyncHolder<StageZero> createStageZeroLoader() { - return new StageZero.DefaultProvider(); - } - - /** @return the provider that loads stage one. */ - protected AsyncHolder<StageOne> createStageOneLoader(StageZero zero) { - return new StageOne.DefaultProvider(zero); - } - - /** @return the provider that loads stage two. */ - protected abstract AsyncHolder<StageTwo> createStageTwoLoader(StageOne one); - - /** @return the provider that loads stage three. */ - protected AsyncHolder<StageThree> createStageThreeLoader(StageTwo two) { - return new StageThree.DefaultProvider(two); - } - - /** - * Initiates the load sequence. - * - * @param whenFinished optional command to run once the last stage is loaded - */ - public final void load(Command whenFinished) { - this.whenFinished = whenFinished; - loadStageZero(); - } - - private void loadStageZero() { - final Timer timer = Timing.start("Stage Zero"); - createStageZeroLoader().call(new Accessor<StageZero>() { - @Override - public void use(StageZero x) { - Timing.stop(timer); - loadStageOne(x); - } - }); - } - - private void loadStageOne(final StageZero zero) { - final Timer timer = Timing.start("Stage One"); - createStageOneLoader(zero).call(new Accessor<StageOne>() { - @Override - public void use(StageOne x) { - Timing.stop(timer); - loadStageTwo(x); - } - }); - } - - - private void loadStageTwo(final StageOne one) { - final Timer timer = Timing.start("Stage Two"); - SchedulerInstance.getHighPriorityTimer().schedule(new Scheduler.Task() { - - @Override - public void execute() { - createStageTwoLoader(one).call(new Accessor<StageTwo>() { - @Override - public void use(StageTwo x) { - Timing.stop(timer); - loadStageThree(x); - } - }); - } - }); - } - - private void loadStageThree(final StageTwo two) { - final Timer timer = Timing.start("Stage Tree"); - SchedulerInstance.getHighPriorityTimer().schedule(new Scheduler.Task() { - - @Override - public void execute() { - createStageThreeLoader(two).call(new Accessor<StageThree>() { - @Override - public void use(StageThree x) { - Timing.stop(timer); - finish(); - } - }); - } - }); - } - - private void finish() { - if (whenFinished != null) { - whenFinished.execute(); - whenFinished = null; - } - } - - private static abstract class SimpleAsyncCallback implements RunAsyncCallback { - @Override - public void onFailure(Throwable reason) { - throw new RuntimeException(reason); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/account/Profile.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/account/Profile.java b/wave/src/main/java/org/waveprotocol/wave/client/account/Profile.java deleted file mode 100644 index 2b8eff9..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/account/Profile.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.account; - -import org.waveprotocol.wave.model.wave.ParticipantId; - -/** - * Profile information for a participant. - * - * @author [email protected] (Benjamin Kalman) - */ -public interface Profile { - - /** - * @return the participant id for this profile - */ - ParticipantId getParticipantId(); - - /** - * @return the address for this profile, same as {@link #getParticipantId()} - */ - String getAddress(); - - /** - * @return the participant's full name - */ - String getFullName(); - - /** - * @return the participant's first name - */ - String getFirstName(); - - /** - * @return the URL of a participant's avatar image - */ - String getImageUrl(); -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/account/ProfileListener.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/account/ProfileListener.java b/wave/src/main/java/org/waveprotocol/wave/client/account/ProfileListener.java deleted file mode 100644 index 8070a06..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/account/ProfileListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.account; - -/** - * Listen to changes in a profile. - * - * @author [email protected] (Benjamin Kalman) - */ -public interface ProfileListener { - void onProfileUpdated(Profile profile); -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/account/ProfileManager.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/account/ProfileManager.java b/wave/src/main/java/org/waveprotocol/wave/client/account/ProfileManager.java deleted file mode 100644 index 1352014..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/account/ProfileManager.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.account; - -import org.waveprotocol.wave.model.wave.ParticipantId; -import org.waveprotocol.wave.model.wave.SourcesEvents; - -/** - * Manages profiles for participants. - * - * @author [email protected] (Benjamin Kalman) - */ -public interface ProfileManager extends SourcesEvents<ProfileListener> { - - /** - * Gets the profile for a participant. - * - * @param participantId id of the participant - * @return the profile for a participant - */ - Profile getProfile(ParticipantId participantId); - - /** - * Returns whether the participant should be ignored in the context of - * accounts. - * - * @param participantId the participant id to check - * @return true if the participant should be ignored, false if not - */ - boolean shouldIgnore(ParticipantId participantId); -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/account/impl/AbstractProfileManager.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/account/impl/AbstractProfileManager.java b/wave/src/main/java/org/waveprotocol/wave/client/account/impl/AbstractProfileManager.java deleted file mode 100644 index 8d7af29..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/account/impl/AbstractProfileManager.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.account.impl; - -import org.waveprotocol.wave.client.account.Profile; -import org.waveprotocol.wave.client.account.ProfileListener; -import org.waveprotocol.wave.client.account.ProfileManager; -import org.waveprotocol.wave.model.util.CollectionUtils; -import org.waveprotocol.wave.model.util.CopyOnWriteSet; -import org.waveprotocol.wave.model.util.StringMap; -import org.waveprotocol.wave.model.wave.ParticipantId; - -/** - * Serves as a base for concrete {@link ProfileManager} implementations. - * - * @author [email protected] (Yuri Zelikov) - */ -public abstract class AbstractProfileManager<P extends Profile> implements ProfileManager { - - protected final StringMap<ProfileImpl> profiles = CollectionUtils.createStringMap(); - protected final CopyOnWriteSet<ProfileListener> listeners = CopyOnWriteSet.create(); - - @Override - public boolean shouldIgnore(ParticipantId participant) { - return false; - } - - @Override - public void addListener(ProfileListener listener) { - listeners.add(listener); - } - - @Override - public void removeListener(ProfileListener listener) { - listeners.remove(listener); - } - - protected void fireOnUpdated(P profile) { - for (ProfileListener listener : listeners) { - listener.onProfileUpdated(profile); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/account/impl/ProfileImpl.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/account/impl/ProfileImpl.java b/wave/src/main/java/org/waveprotocol/wave/client/account/impl/ProfileImpl.java deleted file mode 100644 index 40ecf26..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/account/impl/ProfileImpl.java +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.account.impl; - - -import com.google.common.base.Joiner; - -import org.waveprotocol.wave.client.account.Profile; -import org.waveprotocol.wave.model.util.CollectionUtils; -import org.waveprotocol.wave.model.wave.ParticipantId; - -import java.util.List; - -/** - * A {@link Profile} which determines all properties from just a - * {@link ParticipantId} given on construction. - * - * @author [email protected] (Benjamin Kalman) - */ -public final class ProfileImpl implements Profile { - - private final AbstractProfileManager<? super ProfileImpl> manager; - private final ParticipantId id; - - // Lazily loaded values - private String firstName; - private String fullName; - private String imageUrl; - - public ProfileImpl(AbstractProfileManager<ProfileImpl> manager, ParticipantId id) { - this.manager = manager; - this.id = id; - } - - @Override - public ParticipantId getParticipantId() { - return id; - } - - @Override - public String getAddress() { - return id.getAddress(); - } - - @Override - public String getFullName() { - if (fullName == null) { - buildNames(); - } - return fullName; - } - - @Override - public String getFirstName() { - if (firstName == null) { - buildNames(); - } - return firstName; - } - - @Override - public String getImageUrl() { - if (imageUrl == null) { - imageUrl = "static/images/unknown.jpg"; - } - return imageUrl; - } - - /** - * Attempts to create the fragments of the participant's name from their - * address, for example "[email protected]" into ["John", "Smith"]. - */ - private void buildNames() { - List<String> names = CollectionUtils.newArrayList(); - String nameWithoutDomain = id.getAddress().split("@")[0]; - if (nameWithoutDomain != null && !nameWithoutDomain.isEmpty()) { - // Include empty names from fragment, so split with a -ve. - for (String fragment : nameWithoutDomain.split("[._]", -1)) { - if (!fragment.isEmpty()) { - names.add(capitalize(fragment)); - } - } - // ParticipantId normalization implies names can not be empty. - assert !names.isEmpty(); - firstName = names.get(0); - fullName = Joiner.on(' ').join(names); - } else { - // Name can be empty in case of shared domain participant which has the the form: - // @example.com. - fullName = id.getAddress(); - } - } - - private static String capitalize(String s) { - return s.isEmpty() ? s : (Character.toUpperCase(s.charAt(0))) + s.substring(1); - } - - /** - * Replaces this profile's fields. - * <p> - * Each non-null argument replaces this profile's corresponding field. Null - * arguments have no effect (i.e., they do not clear the existing field). - */ - public void update(String firstName, String fullName, String imageUrl) { - this.firstName = firstName != null ? firstName : this.firstName; - this.fullName = fullName != null ? fullName : this.fullName; - this.imageUrl = imageUrl != null ? imageUrl : this.imageUrl; - - manager.fireOnUpdated(this); - } - - @Override - public String toString() { - return "ProfileImpl [id=" + id + ", firstName=" + firstName + ", fullName=" + fullName - + ", imageUrl=" + imageUrl + "]"; - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/account/impl/ProfileManagerImpl.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/account/impl/ProfileManagerImpl.java b/wave/src/main/java/org/waveprotocol/wave/client/account/impl/ProfileManagerImpl.java deleted file mode 100644 index f23f3cb..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/account/impl/ProfileManagerImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.account.impl; - -import org.waveprotocol.wave.client.account.ProfileManager; -import org.waveprotocol.wave.model.wave.ParticipantId; - -/** - * A {@link ProfileManager} that returns vacuous profiles. - * - * @author [email protected] (Benjamin Kalman) - */ -public final class ProfileManagerImpl extends AbstractProfileManager<ProfileImpl> { - - public ProfileManagerImpl() { - } - - @Override - public ProfileImpl getProfile(ParticipantId participantId) { - ProfileImpl profile = profiles.get(participantId.getAddress()); - if (profile == null) { - profile = new ProfileImpl(this, participantId); - profiles.put(participantId.getAddress(), profile); - } - return profile; - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHider.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHider.java b/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHider.java deleted file mode 100644 index 15a2cc1..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHider.java +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.autohide; - -import com.google.gwt.dom.client.Element; - -import java.util.ArrayList; -import java.util.List; - -/** - * AutoHiders are registered by entities that want to be automatically hidden - * when the user performs a particular global UI action, such as clicking - * outside the containing element of the entity in question. - * - * An AutoHider is registered if it is being applied to incoming events. It is - * possible to have an AutoHider that exists, but is not being used to respond - * to incoming events. - * - */ -public class AutoHider implements Hideable { - /** How sensitive a Hideable is autohide triggers. */ - public enum KeyBehavior { - DO_NOT_HIDE_ON_ANY_KEY, - HIDE_ON_ESCAPE, - /** "Hide on any key" also includes mouse-wheel events as "key event". */ - HIDE_ON_ANY_KEY - } - - /** - * The thing that gets hidden when an appropriate event is detected. - */ - private final Hideable hideable; - - /** - * Whether or not to hide when a click event occurs outside a given element. - */ - private final boolean hideOnOutsideClick; - - /** - * Whether or not to hide when any key (or just the escape key) is pressed. - */ - private final KeyBehavior keyBehavior; - - /** - * Whether or not to hide when the window is resized. - */ - private final boolean hideOnWindowResize; - - /** - * Whether or not to hide when moving in browser history (e.g., back button). - */ - private final boolean hideOnHistoryEvent; - - /** - * Whether or not this AutoHider has been registered and is listening to - * events at the event preview level. - */ - private boolean isRegistered = false; - - /** - * Used to determine whether which parent elements are considered 'inside' the thing being hidden, - * and so clicks must not be contained in these to be considered 'outside' and enough to hide it. - */ - private final List<Element> insideElements = new ArrayList<Element>(); - - /** - * @param hideable Gets hidden on appropriate event. - * @param hideOnOutsideClick Should a click outside the entity cause an auto-hide? - * @param hideOnWindowResize Should resizing the window cause an auto-hide? - * @param hideOnHistoryEvent Should moving back or forward in browser history cause an auto-hide? - * @param keyBehavior Should pressing a key cause an auto-hide? - */ - public AutoHider(Hideable hideable, - boolean hideOnOutsideClick, - boolean hideOnWindowResize, - boolean hideOnHistoryEvent, - KeyBehavior keyBehavior) { - this.hideable = hideable; - this.hideOnOutsideClick = hideOnOutsideClick; - this.hideOnWindowResize = hideOnWindowResize; - this.hideOnHistoryEvent = hideOnHistoryEvent; - this.keyBehavior = keyBehavior; - } - - /** - * @return Whether or not we should hide when any key is pressed. - */ - public boolean shouldHideOnAnyKey() { - return keyBehavior == KeyBehavior.HIDE_ON_ANY_KEY; - } - - /** - * @return Whether or not we should hide when the escape key is pressed. - */ - public boolean shouldHideOnEscape() { - return keyBehavior == KeyBehavior.HIDE_ON_ESCAPE || keyBehavior == KeyBehavior.HIDE_ON_ANY_KEY; - } - - /** - * @return Whether or not we should hide when a click occurs outside of the - * entity. - */ - public boolean shouldHideOnOutsideClick() { - return hideOnOutsideClick; - } - - /** - * @return Whether or not we should hide when the window is resized. - */ - public boolean shouldHideOnWindowResize() { - return hideOnWindowResize; - } - - /** - * @return Whether or not we should hide when moving in browser history - */ - public boolean shouldHideOnHistoryEvent() { - return hideOnHistoryEvent; - } - - /** - * @return Whether or not this AutoHider is registered. - */ - public boolean isRegistered() { - return isRegistered; - } - - /** - * Whitelists an element so that clicking on it does not hide the item this hider is for. - * - * @param element The element to use to determine whether click events are - * inside or outside. - */ - public void ignoreHideClickFor(Element element) { - this.insideElements.add(element); - } - - /** - * @param isRegistered Whether or not this AutoHider is registered. - */ - public void setRegistered(boolean isRegistered) { - this.isRegistered = isRegistered; - } - - @Override - public void hide() { - hideable.hide(); - } - - @Override - public boolean isShowing() { - return hideable.isShowing(); - } - - /** - * @param target An element. - * @return {@code true} if the given element is considered to be inside the - * entity. - */ - public boolean doesContain(Element target) { - for (Element element : insideElements) { - if (element.isOrHasChild(target)) { - return true; - } - } - return false; - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHiderRegistrar.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHiderRegistrar.java b/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHiderRegistrar.java deleted file mode 100644 index ec26d07..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHiderRegistrar.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.autohide; - -/** - * Interface for things that use AutoHiders to interpret incoming events and - * notify AutoHiders when they should hide. - * - */ -public interface AutoHiderRegistrar { - /** - * @param autoHider AutoHider that should be considered when processing - * incoming events. - */ - void registerAutoHider(AutoHider autoHider); - - /** - * @param autoHider AutoHider that should no longer be considered when - * processing incoming events. - */ - void deregisterAutoHider(AutoHider autoHider); -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHiderRegistrarHolder.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHiderRegistrarHolder.java b/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHiderRegistrarHolder.java deleted file mode 100644 index f526d94..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/autohide/AutoHiderRegistrarHolder.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.autohide; - -/** - * Provides access to a singleton AutoHiderRegistrar. - * - */ -public class AutoHiderRegistrarHolder { - /** - * The singleton AutoHideRegistrar. - */ - private static AutoHiderRegistrar autoHideRegistrar = new EventPreviewAutoHiderRegistrar(); - - /** - * @return The singleton AutoHideRegistrar. - */ - public static AutoHiderRegistrar get() { - return autoHideRegistrar; - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/autohide/EventPreviewAutoHiderRegistrar.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/autohide/EventPreviewAutoHiderRegistrar.java b/wave/src/main/java/org/waveprotocol/wave/client/autohide/EventPreviewAutoHiderRegistrar.java deleted file mode 100644 index 72dc694..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/autohide/EventPreviewAutoHiderRegistrar.java +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.autohide; - -import com.google.gwt.dom.client.Element; -import com.google.gwt.event.dom.client.KeyCodes; -import com.google.gwt.event.logical.shared.ResizeEvent; -import com.google.gwt.event.logical.shared.ResizeHandler; -import com.google.gwt.event.logical.shared.ValueChangeEvent; -import com.google.gwt.event.logical.shared.ValueChangeHandler; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.History; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.Event.NativePreviewEvent; -import com.google.gwt.user.client.Event.NativePreviewHandler; -import org.waveprotocol.wave.client.common.util.SignalEvent; -import org.waveprotocol.wave.client.common.util.SignalEventImpl; -import org.waveprotocol.wave.client.common.util.SignalEvent.KeySignalType; - -import java.util.ArrayList; -import java.util.List; - -/** - * Detects auto-hide events by installing an event preview. - * - * This registrar handles multiple simultaneous AutoHiders by treating the ones - * opened later as 'higher up' than the ones opened earlier, and so events that - * cause auto-hides will be routed to the higher AutoHiders first. This - * behaviour can be changed in the future by introducing a more sophisticated - * structure to store the AutoHiders in and, for instance, having each AutoHider - * declaring a parent AutoHider. - * - */ -public class EventPreviewAutoHiderRegistrar implements AutoHiderRegistrar, NativePreviewHandler, -ResizeHandler, ValueChangeHandler<String> { - /** - * List of AutoHiders that currently need to be considered when interpreting - * incoming events. - */ - private final List<AutoHider> autoHiders = new ArrayList<AutoHider>(); - - /** - * Used to deregister this object from the event preview when there are no - * registered AutoHiders. - */ - private HandlerRegistration eventPreviewRegistration; - - private HandlerRegistration onResizeRegistration; - - private HandlerRegistration onHistoryRegistration; - - @Override - public void registerAutoHider(final AutoHider autoHider) { - autoHider.setRegistered(true); - autoHiders.add(autoHider); - - if (eventPreviewRegistration == null) { - eventPreviewRegistration = Event.addNativePreviewHandler(this); - } - - if (onResizeRegistration == null) { - onResizeRegistration = Window.addResizeHandler(this); - } - - if (onHistoryRegistration == null) { - onHistoryRegistration = History.addValueChangeHandler(this); - } - } - - @Override - public void deregisterAutoHider(AutoHider autoHider) { - autoHiders.remove(autoHider); - autoHider.setRegistered(false); - - if (autoHiders.isEmpty()) { - if (eventPreviewRegistration != null) { - eventPreviewRegistration.removeHandler(); - eventPreviewRegistration = null; - } - if (onResizeRegistration != null) { - onResizeRegistration.removeHandler(); - onResizeRegistration = null; - } - if (onHistoryRegistration!= null) { - onHistoryRegistration.removeHandler(); - onHistoryRegistration = null; - } - } - } - - @Override - public void onPreviewNativeEvent(NativePreviewEvent previewEvent) { - if (autoHiders.isEmpty()) { - return; - } - - // TODO(danilatos,user,user): Push signal down a layer - clean this up. - Event event = Event.as(previewEvent.getNativeEvent()); - int lowLevelType = event.getTypeInt(); - - // TODO(danilatos): Insert this logic deeply rather than - // sprinkling it in event handlers. Also the return value - // of onEventPreview is the reverse of signal handlers. - SignalEvent signal = SignalEventImpl.create(event, false); - if (signal == null) { - return; - } - - // Key events (excluding escape) and mousewheel events use hideTopmostAutoHiderForKeyEvent - if (lowLevelType == Event.ONMOUSEWHEEL || signal.isKeyEvent()) { - if (hideTopmostAutoHiderForKeyEvent(false)) { - // TODO(user): We don't call previewEvent.cancel() here, since for the floating-buttons - // menu we want, for example, space-bar to still shift focus to the next blip. - // The to-do is to audit the previewEvent.cancel call below and see why it's there (and if - // it's not needed, eliminate it). - return; - } - } - - // Pressing escape at any time causes us to close and discard the event. - if (signal.getKeySignalType() == KeySignalType.NOEFFECT && - event.getKeyCode() == KeyCodes.KEY_ESCAPE) { - if (hideTopmostAutoHiderForKeyEvent(true)) { - previewEvent.cancel(); - return; - } - } - - // Click events and mouse-wheel events that fall through use hideAllAfter. - if (lowLevelType == Event.ONMOUSEDOWN || lowLevelType == Event.ONMOUSEWHEEL) { - hideAllAfter(signal.getTarget()); - } - - // Otherwise we don't do anything and the event continues as usual. - } - - /** - * Causes all AutoHiders after the one that contains the given element to hide. - * - * @param target An element. - */ - private void hideAllAfter(Element target) { - List<AutoHider> toHide = new ArrayList<AutoHider>(); - for (int i = autoHiders.size() - 1; i >= 0; i--) { - AutoHider autoHider = autoHiders.get(i); - if (autoHider.doesContain(target)) { - break; - } - toHide.add(autoHider); - } - - for (AutoHider autoHider : toHide) { - autoHider.hide(); - } - } - - /** - * Hides the topmost AutoHider that is supposed to hide on key events. - */ - private boolean hideTopmostAutoHiderForKeyEvent(boolean keyIsEscape) { - for (int i = autoHiders.size() - 1; i >= 0; i--) { - AutoHider autoHider = autoHiders.get(i); - if (autoHider.shouldHideOnAnyKey() || (keyIsEscape && autoHider.shouldHideOnEscape())) { - autoHider.hide(); - return true; - } - } - return false; - } - - @Override - public void onResize(ResizeEvent event) { - List<AutoHider> toHide = new ArrayList<AutoHider>(); - for (AutoHider autoHider : autoHiders) { - if (autoHider.shouldHideOnWindowResize()) { - toHide.add(autoHider); - } - } - - for (AutoHider autoHider : toHide) { - autoHider.hide(); - } - } - - @Override - public void onValueChange(ValueChangeEvent<String> event) { - List<AutoHider> toHide = new ArrayList<AutoHider>(); - for (AutoHider autoHider : autoHiders) { - if (autoHider.shouldHideOnHistoryEvent()) { - toHide.add(autoHider); - } - } - - for (AutoHider autoHider : toHide) { - autoHider.hide(); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/autohide/Hideable.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/autohide/Hideable.java b/wave/src/main/java/org/waveprotocol/wave/client/autohide/Hideable.java deleted file mode 100644 index b641389..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/autohide/Hideable.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.autohide; - -/** - * Something that can be hidden. - * - */ -public interface Hideable { - /** - * @return {@code true} if the object is showing. - */ - boolean isShowing(); - - /** - * Hides the object. - */ - void hide(); -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/clipboard/AnnotationSerializer.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/clipboard/AnnotationSerializer.java b/wave/src/main/java/org/waveprotocol/wave/client/clipboard/AnnotationSerializer.java deleted file mode 100644 index c248ec0..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/clipboard/AnnotationSerializer.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.clipboard; - -import com.google.gwt.http.client.URL; -import org.waveprotocol.wave.model.document.RangedAnnotation; -import org.waveprotocol.wave.model.document.util.RangedAnnotationImpl; - -import java.util.ArrayList; -import java.util.List; - -/** - * Serialization/deserialization for annotations. - * - * Annotation format (There is no guarantee this will stay): - * - * Each annotation contains start, end, url escaped key, url escaped value - * separated by "," The annotations are separated by ":" - * - * Example : "23,39,fontWeight,bold:59,64,fontStyle,italics" - * - * TODO(user): Move this to a common package, as this seems generally useful. - * But first, remove the dependency on gwt. - * - */ -public class AnnotationSerializer { - /** - * Deserializes the string into a list of ranged annotations. - * - * @param serialized - */ - public static List<RangedAnnotation<String>> deserialize(String serialized) { - String[] split = serialized.split(OUTER_SEPARATOR); - List<RangedAnnotation<String>> annotations = new ArrayList<RangedAnnotation<String>>(); - for (String s : split) { - if (!s.isEmpty()) { - String innerSplit[] = s.split(INNER_SEPARATOR); - if (innerSplit.length == 4) { - annotations.add(new RangedAnnotationImpl<String>(unescape(innerSplit[2]), - unescape(innerSplit[3]), Integer.parseInt(innerSplit[0]), Integer - .parseInt(innerSplit[1]))); - } - } - } - return annotations; - } - - /** Utility class */ - private AnnotationSerializer() {} - - /** - * Serializes a list of rangedAnnotations - * - * @param rangedAnnotations - */ - public static String serializeAnnotation(Iterable<RangedAnnotation<String>> rangedAnnotations) { - Builder entries = new Builder(); - for (RangedAnnotation<String> ann : rangedAnnotations) { - if (ann.value() != null) { - entries.pushEntry(ann.start(), ann.end(), ann.key(), ann.value()); - } - } - return entries.toString(); - } - - - // TODO(user): Consider some other encoding format. This is ok, but is gwt - // dependent. - private static final String INNER_SEPARATOR = ","; - private static final String OUTER_SEPARATOR = ":"; - - private static String escape(String s) { - return URL.encodeComponent(s); - } - - private static String unescape(String s) { - return URL.decodeComponent(s); - } - - private static class Builder { - StringBuilder builder = new StringBuilder(); - - public void pushEntry(int start, int end, String key, String value) { - builder - .append(start).append(INNER_SEPARATOR) - .append(end).append(INNER_SEPARATOR) - .append(escape(key)).append(INNER_SEPARATOR) - .append(escape(value)).append(OUTER_SEPARATOR); - } - - @Override - public String toString() { - return builder.toString(); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/org/waveprotocol/wave/client/clipboard/Clipboard.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/org/waveprotocol/wave/client/clipboard/Clipboard.java b/wave/src/main/java/org/waveprotocol/wave/client/clipboard/Clipboard.java deleted file mode 100644 index 46e72d2..0000000 --- a/wave/src/main/java/org/waveprotocol/wave/client/clipboard/Clipboard.java +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.clipboard; - -import com.google.gwt.dom.client.Document; -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.Node; -import com.google.gwt.dom.client.NodeList; -import com.google.gwt.dom.client.SpanElement; -import com.google.gwt.dom.client.Text; -import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.DeferredCommand; - -import org.waveprotocol.wave.client.common.util.DomHelper; -import org.waveprotocol.wave.client.debug.logger.DomLogger; -import org.waveprotocol.wave.client.editor.selection.html.NativeSelectionUtil; -import org.waveprotocol.wave.common.logging.LoggerBundle; -import org.waveprotocol.wave.model.document.RangedAnnotation; -import org.waveprotocol.wave.model.document.util.FocusedPointRange; -import org.waveprotocol.wave.model.document.util.Point; -import org.waveprotocol.wave.model.document.util.PointRange; -import org.waveprotocol.wave.model.document.util.XmlStringBuilder; - -/** - * A html clipboard that supports wave metadata: wave xml and annotations - * - * TODO(user): Add high level methods for extracting semantic content from - * clipboard. - * - */ -public class Clipboard { - private static final String MAGIC_CLASSNAME = "__wave_paste"; - private static final String WAVE_XML_ATTRIBUTE = "data-wave-xml"; - private static final String WAVE_ANNOTATIONS_ATTRIBUTE = "data-wave-annotations"; - - public static final LoggerBundle LOG = new DomLogger("clipboard"); - - /** - * Singleton instance of clipboard, there should only be 1 per client. - */ - private static final Clipboard INSTANCE = new Clipboard(PasteBufferImpl.create()); - - /** - * Underlying pasteBuffer. - */ - private final PasteBufferImpl pasteBuffer; - - public static final Clipboard get() { - return INSTANCE; - } - - private Clipboard(PasteBufferImpl pasteBuffer) { - this.pasteBuffer = pasteBuffer; - } - - /** - * Fill the PasteBuffer with the given content. - * - * This fills the PasteBuffer and sets the selection over the corresponding - * html on the buffer. To fill the clipboard, this needs to be called inside a - * copy event handler, or follow this with an execCommand(copy) - * - * @param htmlFragment - * @param selection - * @param waveXml - * @param normalizedAnnotation - */ - public void fillBufferAndSetSelection(Node htmlFragment, PointRange<Node> selection, - XmlStringBuilder waveXml, Iterable<RangedAnnotation<String>> normalizedAnnotation, - boolean restoreSelection) { - final FocusedPointRange<Node> oldSelection = restoreSelection ? NativeSelectionUtil.get() : null; - - // Clear this node and append the cloned fragment. - pasteBuffer.setContent(htmlFragment); - - assert htmlFragment.isOrHasChild(selection.getFirst().getContainer()) : - "first not attached before hijack"; - assert htmlFragment.isOrHasChild(selection.getSecond().getContainer()) : - "second not attached before hijack"; - - if (waveXml != null && normalizedAnnotation != null) { - selection = - hijackFragment(waveXml.toString(), AnnotationSerializer - .serializeAnnotation(normalizedAnnotation), selection); - } - assert htmlFragment.isOrHasChild(selection.getFirst().getContainer()) : - "first not attached after hijack"; - assert htmlFragment.isOrHasChild(selection.getSecond().getContainer()) : - "second not attached after hijack"; - NativeSelectionUtil.set(selection.getFirst(), selection.getSecond()); - - if (restoreSelection && oldSelection != null) { - DeferredCommand.addCommand(new Command() { - @Override - public void execute() { - NativeSelectionUtil.set(oldSelection); - } - }); - } - } - - /** - * {@link #fillBufferAndSetSelection(Node, PointRange, XmlStringBuilder, Iterable, boolean) - * same as above, but with the entire fragment. } - * - * @param htmlFragment - * @param waveXml - * @param normalizedAnnotation - */ - public void fillBufferAndSetSelection(Node htmlFragment, XmlStringBuilder waveXml, - Iterable<RangedAnnotation<String>> normalizedAnnotation, boolean restoreSelection) { - Element parent = Document.get().createDivElement(); - parent.appendChild(htmlFragment); - PointRange<Node> selection = - new PointRange<Node>(Point.inElement(parent, htmlFragment), Point.<Node> end(parent)); - fillBufferAndSetSelection(parent, selection, waveXml, normalizedAnnotation, restoreSelection); - } - - /** - * Extract waveXml from this container. - * - * @param srcContainer - */ - public String maybeGetWaveXml(Element srcContainer) { - String waveXml = maybeGetAttributeFromContainer(srcContainer, WAVE_XML_ATTRIBUTE); - String x = srcContainer.getInnerHTML(); - if (waveXml != null) { - LOG.trace().logPlainText("found serialized waveXml: " + waveXml); - - // NOTE(user): Ensure waveXml does not contain any new line characters. - // FF36+ adds random new lines the attributes - // https://bugzilla.mozilla.org/show_bug.cgi?id=540979 - // We filter on all browsers, so they can accept content from FF36. - waveXml = waveXml.replace("\n", ""); - } - - return waveXml; - } - - /** - * Extract serialized annotations from this container. - * - * @param srcContainer - */ - public String maybeGetAnnotations(Element srcContainer) { - String annotations = maybeGetAttributeFromContainer(srcContainer, WAVE_ANNOTATIONS_ATTRIBUTE); - if (annotations != null) { - LOG.trace().log("found serialized annotations: " + annotations); - } - return annotations; - } - - private String maybeGetAttributeFromContainer(Element srcContainer, String attribName) { - NodeList<Element> elementsByClassName = - DomHelper.getElementsByClassName(srcContainer, MAGIC_CLASSNAME); - if (elementsByClassName != null && elementsByClassName.getLength() > 0) { - return elementsByClassName.getItem(0).getAttribute(attribName); - } - return null; - } - - /** - * Return the underlying paste buffer. - * - * NOTE(user): Paste buffer functionality should be abstracted into this - * class. - */ - public PasteBufferImpl getPasteBuffer() { - return pasteBuffer; - } - - /** - * Hijacks the paste fragment by hiding a span with metadata at the end of the - * fragment. - * - * @param xmlInRange The xml string to pass into the magic span element - * @param annotations The annotation string to pass into the magic span - * element - * @param origRange the current range. The span element will be inserted - * before the start - * - * @return The new adjusted selection. The end will be adjusted such that it - * encloses the original selection and the span with metadata - */ - private PointRange<Node> hijackFragment(String xmlInRange, String annotations, - PointRange<Node> origRange) { - Point<Node> origStart = origRange.getFirst(); - Point<Node> origEnd = origRange.getSecond(); - SpanElement spanForXml = Document.get().createSpanElement(); - spanForXml.setAttribute(WAVE_XML_ATTRIBUTE, xmlInRange); - spanForXml.setAttribute(WAVE_ANNOTATIONS_ATTRIBUTE, annotations); - spanForXml.setClassName(MAGIC_CLASSNAME); - - LOG.trace().log("original point: " + origStart); - - // NOTE(user): An extra span is required at the end for Safari, otherwise - // the span with the metadata may get discarded. - SpanElement trailingSpan = Document.get().createSpanElement(); - trailingSpan.setInnerHTML(" "); - - if (origEnd.isInTextNode()) { - Text t = (Text) origEnd.getContainer(); - t.setData(t.getData().substring(0, origEnd.getTextOffset())); - origEnd.getContainer().getParentElement().insertAfter(spanForXml, t); - origEnd.getContainer().getParentElement().insertAfter(trailingSpan, spanForXml); - } else { - origEnd.getContainer().insertAfter(spanForXml, origEnd.getNodeAfter()); - origEnd.getContainer().insertAfter(trailingSpan, spanForXml); - } - - - Point<Node> newEnd = - Point.<Node> inElement(spanForXml.getParentElement(), trailingSpan.getNextSibling()); - LOG.trace().log("new point: " + newEnd); - LOG.trace().logPlainText("parent: " + spanForXml.getParentElement().getInnerHTML()); - assert newEnd.getNodeAfter() == null - || newEnd.getNodeAfter().getParentElement() == newEnd.getContainer() : "inconsistent point"; - return new PointRange<Node>(origStart, newEnd); - } -}
