/** * 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.view.dom; import static org.waveprotocol.wave.client.uibuilder.BuilderHelper.KIND_ATTRIBUTE; import static org.waveprotocol.wave.client.wavepanel.view.dom.DomViewHelper.attachAfter; import static org.waveprotocol.wave.client.wavepanel.view.dom.DomViewHelper.attachBefore; import static org.waveprotocol.wave.client.wavepanel.view.dom.DomViewHelper.getAfter; import static org.waveprotocol.wave.client.wavepanel.view.dom.DomViewHelper.getBefore; import static org.waveprotocol.wave.client.wavepanel.view.dom.full.TypeCodes.kind; import static org.waveprotocol.wave.client.wavepanel.view.dom.full.TypeCodes.type; import com.google.common.base.Preconditions; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Display; import org.waveprotocol.wave.client.common.util.UserAgent; import org.waveprotocol.wave.client.scroll.DomScrollPanel; import org.waveprotocol.wave.client.scroll.Extent; import org.waveprotocol.wave.client.scroll.ScrollPanel; import org.waveprotocol.wave.client.wavepanel.view.AnchorView; import org.waveprotocol.wave.client.wavepanel.view.BlipLinkPopupView; import org.waveprotocol.wave.client.wavepanel.view.BlipMetaView; import org.waveprotocol.wave.client.wavepanel.view.BlipView; import org.waveprotocol.wave.client.wavepanel.view.ContinuationIndicatorView; import org.waveprotocol.wave.client.wavepanel.view.ConversationView; import org.waveprotocol.wave.client.wavepanel.view.FocusFrameView; import org.waveprotocol.wave.client.wavepanel.view.InlineConversationView; import org.waveprotocol.wave.client.wavepanel.view.InlineThreadView; import org.waveprotocol.wave.client.wavepanel.view.ParticipantView; import org.waveprotocol.wave.client.wavepanel.view.ParticipantsView; import org.waveprotocol.wave.client.wavepanel.view.ReplyBoxView; import org.waveprotocol.wave.client.wavepanel.view.RootThreadView; import org.waveprotocol.wave.client.wavepanel.view.ThreadView; import org.waveprotocol.wave.client.wavepanel.view.View; import org.waveprotocol.wave.client.wavepanel.view.View.Type; import org.waveprotocol.wave.client.wavepanel.view.dom.full.BlipLinkPopupWidget; import org.waveprotocol.wave.client.wavepanel.view.dom.full.DomRenderer; import org.waveprotocol.wave.client.wavepanel.view.dom.full.FocusFrame; import org.waveprotocol.wave.client.wavepanel.view.impl.AbstractConversationViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.AbstractStructuredView; import org.waveprotocol.wave.client.wavepanel.view.impl.AnchorViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.BlipMenuItemViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.BlipMetaViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.BlipViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.ContinuationIndicatorViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.InlineConversationViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.InlineThreadViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.ParticipantViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.ParticipantsViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.ReplyBoxViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.RootThreadViewImpl; import org.waveprotocol.wave.client.wavepanel.view.impl.TopConversationViewImpl; import org.waveprotocol.wave.client.widget.popup.AlignedPopupPositioner; import org.waveprotocol.wave.client.widget.profile.ProfilePopupView; import org.waveprotocol.wave.client.widget.profile.ProfilePopupWidget; import org.waveprotocol.wave.model.conversation.Conversation; import org.waveprotocol.wave.model.conversation.ConversationBlip; import org.waveprotocol.wave.model.conversation.ConversationThread; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.ReadableStringSet; import org.waveprotocol.wave.model.util.StringSet; import org.waveprotocol.wave.model.wave.ParticipantId; /** * Implements structural accessors and mutators for wave-panel views. * */ public final class FullStructure implements UpgradeableDomAsViewProvider { /** Mapping of kinds to types. */ private static final ReadableStringSet KNOWN_KINDS; static { StringSet kinds = CollectionUtils.createStringSet(); for (Type type : new Type[] {Type.BLIP, Type.META, Type.MENU_ITEM, Type.ROOT_THREAD, Type.REPLY_BOX, Type.INLINE_THREAD, Type.CONTINUATION_INDICATOR, Type.ANCHOR, Type.ROOT_CONVERSATION, Type.INLINE_CONVERSATION, Type.PARTICIPANT, Type.PARTICIPANTS}) { kinds.add(kind(type)); } KNOWN_KINDS = kinds; } private final BlipMenuItemViewImpl.Helper<BlipMenuItemDomImpl> menuHelper = new BlipMenuItemViewImpl.Helper<BlipMenuItemDomImpl>() { @Override public BlipMetaView getParent(BlipMenuItemDomImpl impl) { return asBlipMeta(parentOf(impl)); } @Override public void remove(BlipMenuItemDomImpl impl) { impl.remove(); } }; private final BlipMetaViewImpl.Helper<BlipMetaDomImpl> metaHelper = new BlipMetaViewImpl.Helper<BlipMetaDomImpl>() { private Element frameElement(FocusFrameView frame) { return ((FocusFrame) frame).getElement(); } @Override public void insertChrome(BlipMetaDomImpl impl, FocusFrameView frame) { impl.getElement().appendChild(frameElement(frame)); } @Override public void removeChrome(BlipMetaDomImpl impl, FocusFrameView frame) { frameElement(frame).removeFromParent(); } @Override public AnchorView getInlineAnchorAfter(BlipMetaDomImpl impl, AnchorView ref) { return asAnchor(impl.getInlineAnchorAfter(elementOf(ref))); } @Override public AnchorView getInlineAnchorBefore(BlipMetaDomImpl impl, AnchorView ref) { return asAnchor(impl.getInlineAnchorBefore(elementOf(ref))); } @Override public void insertInlineAnchorBefore(BlipMetaDomImpl impl, AnchorView ref, AnchorView x) { @SuppressWarnings("unchecked") AnchorDomImpl anchor = ((AnchorViewImpl<AnchorDomImpl>) x).getIntrinsic(); impl.insertInlineLocatorBefore(elementOf(ref), anchor.getElement()); anchor.setParentId(impl.getId()); } @Override public BlipView getBlip(BlipMetaDomImpl impl) { return asBlip(parentOf(impl)); } @Override public void remove(BlipMetaDomImpl impl) { impl.remove(); } }; private final BlipViewImpl.Helper<BlipViewDomImpl> blipHelper = new BlipViewImpl.Helper<BlipViewDomImpl>() { @Override public ThreadView getBlipParent(BlipViewDomImpl thread) { return asThread(parentOf(thread)); } @Override public BlipMetaViewImpl<BlipMetaDomImpl> getMeta(BlipViewDomImpl impl) { return asBlipMeta(impl.getMetaHolder()); } @Override public AnchorView getDefaultAnchorAfter(BlipViewDomImpl impl, AnchorView ref) { return asAnchor(getAfter(impl.getDefaultAnchors(), elementOf(ref))); } @Override public AnchorView getDefaultAnchorBefore(BlipViewDomImpl impl, AnchorView ref) { return asAnchor(getBefore(impl.getDefaultAnchors(), elementOf(ref))); } @Override public AnchorView insertDefaultAnchorBefore( BlipViewDomImpl impl, AnchorView ref, ConversationThread t) { Element anchorDom = getRenderer().render(t); attachBefore(impl.getDefaultAnchors(), elementOf(ref), anchorDom); return asAnchorUnchecked(anchorDom); } @Override public AnchorView insertDefaultAnchorAfter( BlipViewDomImpl impl, AnchorView ref, ConversationThread t) { Element anchorDom = getRenderer().render(t); attachAfter(impl.getDefaultAnchors(), elementOf(ref), anchorDom); return asAnchorUnchecked(anchorDom); } @Override public InlineConversationView getConversationBefore( BlipViewDomImpl impl, InlineConversationView ref) { return asInlineConversation(getBefore(impl.getConversations(), elementOf(ref))); } @Override public InlineConversationView getConversationAfter( BlipViewDomImpl impl, InlineConversationView ref) { return asInlineConversation(getAfter(impl.getConversations(), elementOf(ref))); } @Override public InlineConversationView insertConversationBefore( BlipViewDomImpl impl, InlineConversationView ref, Conversation c) { Element convDom = getRenderer().render(c); attachBefore(impl.getConversations(), elementOf(ref), convDom); return asInlineConversationUnchecked(convDom); } @Override public void removeBlip(BlipViewDomImpl impl) { impl.remove(); } @Override public BlipLinkPopupView createLinkPopup(BlipViewDomImpl impl) { return new BlipLinkPopupWidget(impl.getElement()); } }; private final RootThreadViewImpl.Helper<RootThreadDomImpl> rootThreadHelper = new RootThreadViewImpl.Helper<RootThreadDomImpl>() { @Override public BlipView getBlipBefore(RootThreadDomImpl thread, View ref) { return asBlip(getBefore(thread.getBlipContainer(), elementOf(ref))); } @Override public BlipView getBlipAfter(RootThreadDomImpl thread, View ref) { return asBlip(getAfter(thread.getBlipContainer(), elementOf(ref))); } @Override public ConversationView getThreadParent(RootThreadDomImpl thread) { return asConversation(parentOf(thread)); } @Override public BlipView insertBlipBefore( RootThreadDomImpl thread, View ref, ConversationBlip blip) { Element t = getRenderer().render(blip); thread.getBlipContainer().insertBefore(t, elementOf(ref)); return asBlip(t); } @Override public BlipView insertBlipAfter( RootThreadDomImpl thread, View ref, ConversationBlip blip) { Element t = getRenderer().render(blip); thread.getBlipContainer().insertAfter(t, elementOf(ref)); return asBlip(t); } @Override public void removeThread(RootThreadDomImpl thread) { thread.remove(); } @Override public ReplyBoxView getIndicator(RootThreadDomImpl impl) { return asReplyBox(impl.getIndicator()); } }; private final ReplyBoxViewImpl.Helper<ReplyBoxDomImpl> rootThreadIndicatorHelper = new ReplyBoxViewImpl.Helper<ReplyBoxDomImpl>() { @Override public RootThreadView getParent(ReplyBoxDomImpl impl) { return asRootThread(parentOf(impl)); } @Override public void remove(ReplyBoxDomImpl impl) { impl.remove(); } }; private final InlineThreadViewImpl.Helper<InlineThreadDomImpl> inlineThreadHelper = new InlineThreadViewImpl.Helper<InlineThreadDomImpl>() { @Override public BlipView getBlipBefore(InlineThreadDomImpl thread, View ref) { return asBlip(getBefore(thread.getBlipContainer(), elementOf(ref))); } @Override public BlipView getBlipAfter(InlineThreadDomImpl thread, View ref) { return asBlip(getAfter(thread.getBlipContainer(), elementOf(ref))); } @Override public AnchorView getThreadParent(InlineThreadDomImpl thread) { return asAnchor(parentOf(thread)); } @Override public BlipView insertBlipBefore( InlineThreadDomImpl thread, View ref, ConversationBlip blip) { Element t = getRenderer().render(blip); thread.getBlipContainer().insertBefore(t, elementOf(ref)); return asBlip(t); } @Override public BlipView insertBlipAfter( InlineThreadDomImpl thread, View ref, ConversationBlip blip) { Element t = getRenderer().render(blip); thread.getBlipContainer().insertAfter(t, elementOf(ref)); return asBlip(t); } @Override public void removeThread(InlineThreadDomImpl thread) { thread.remove(); } @Override public ContinuationIndicatorView getIndicator(InlineThreadDomImpl thread) { return asContinuationIndicator(thread.getContinuationIndicator()); } }; private final ContinuationIndicatorViewImpl.Helper<ContinuationIndicatorDomImpl> inlineThreadIndicatorHelper = new ContinuationIndicatorViewImpl.Helper<ContinuationIndicatorDomImpl>() { @Override public InlineThreadView getParent(ContinuationIndicatorDomImpl impl) { return asInlineThread(parentOf(impl)); } @Override public void remove(ContinuationIndicatorDomImpl impl) { impl.remove(); } }; private final AnchorViewImpl.Helper<AnchorDomImpl> anchorHelper = new AnchorViewImpl.Helper<AnchorDomImpl>() { @Override public void attach(AnchorDomImpl anchor, InlineThreadView thread) { anchor.setChild(elementOf(thread)); } @Override public void detach(AnchorDomImpl anchor, InlineThreadView thread) { anchor.removeChild(elementOf(thread)); } @Override public InlineThreadView getThread(AnchorDomImpl anchor) { Element child = anchor.getChild(); return asInlineThread(child); } @Override public void remove(AnchorDomImpl impl) { View parent = getParent(impl); if (parent.getType() == Type.BLIP) { BlipViewDomImpl parentImpl = ((BlipViewDomImpl) narrow(parent).getIntrinsic()); DomViewHelper.detach(parentImpl.getDefaultAnchors(), impl.getElement()); } else if (parent.getType() == Type.META) { BlipMetaDomImpl parentImpl = ((BlipMetaDomImpl) narrow(parent).getIntrinsic()); parentImpl.removeInlineLocator(impl.getElement()); // Do not detach here - editor rendering controls that. impl.setParentId(null); } } @Override public View getParent(AnchorDomImpl impl) { String pid = impl.getParentId(); return pid != null ? viewOf(Document.get().getElementById(pid)) : parentOf(impl); } }; private final TopConversationViewImpl.Helper<TopConversationDomImpl> convHelper = new TopConversationViewImpl.Helper<TopConversationDomImpl>() { private ScrollPanel<Element> createDomScroller(TopConversationDomImpl impl) { return DomScrollPanel.create(impl.getThreadContainer()); } @Override public ScrollPanel<? super View> getScroller(TopConversationDomImpl impl) { final ScrollPanel<Element> domScroller = createDomScroller(impl); return new ScrollPanel<View>() { @Override public Extent extentOf(View ui) { return domScroller.extentOf(elementOf(ui)); } @Override public Extent getContent() { return domScroller.getContent(); } @Override public Extent getViewport() { return domScroller.getViewport(); } @Override public void moveTo(double location) { domScroller.moveTo(location); } }; } @Override public void setToolbar(TopConversationDomImpl impl, Element e) { impl.setToolbar(e); } @Override public ParticipantsView getParticipants(TopConversationDomImpl impl) { return asParticipants(impl.getParticipants()); } @Override public RootThreadView getRootThread(TopConversationDomImpl impl) { return asRootThread(impl.getThread()); } @Override public void remove(TopConversationDomImpl impl) { impl.remove(); } }; private final InlineConversationViewImpl.Helper<InlineConversationDomImpl> inlineConvHelper = new InlineConversationViewImpl.Helper<InlineConversationDomImpl>() { @Override public ParticipantsView getParticipants(InlineConversationDomImpl impl) { return asParticipants(impl.getParticipants()); } @Override public RootThreadView getRootThread(InlineConversationDomImpl impl) { return asRootThread(impl.getRootThread()); } @Override public void remove(InlineConversationDomImpl impl) { impl.remove(); } @Override public BlipView getParent(InlineConversationDomImpl impl) { return asBlip(parentOf(impl)); } }; private final ParticipantViewImpl.Helper<ParticipantNameDomImpl> participantHelper = new ParticipantViewImpl.Helper<ParticipantNameDomImpl>() { @Override public void remove(ParticipantNameDomImpl impl) { Element container = impl.getElement().getParentElement(); impl.remove(); // Kick Webkit, because of its incremental layout bugs. if (UserAgent.isWebkit()) { // Erase layout. Querying getOffsetParent() forces layout. container.getStyle().setDisplay(Display.NONE); container.getOffsetParent(); // Restore layout. container.getStyle().clearDisplay(); } } @Override public ProfilePopupView showParticipation(ParticipantNameDomImpl impl) { return new ProfilePopupWidget(impl.getElement(), AlignedPopupPositioner.BELOW_RIGHT); } }; private final ParticipantsViewImpl.Helper<ParticipantsDomImpl> participantsHelper = new ParticipantsViewImpl.Helper<ParticipantsDomImpl>() { @Override public void remove(ParticipantsDomImpl impl) { impl.remove(); } @Override public ParticipantView append( ParticipantsDomImpl impl, Conversation conv, ParticipantId participant) { Element t = getRenderer().render(conv, participant); DomViewHelper.attachBefore(impl.getParticipantContainer(), impl.getSimpleMenu(), t); // Kick Webkit, because of its incremental layout bugs. if (UserAgent.isWebkit()) { String oldDisplay = impl.getElement().getStyle().getDisplay(); // Erase layout. Querying getOffsetParent() forces layout. impl.getElement().getStyle().setDisplay(Display.NONE); impl.getElement().getOffsetParent(); // Restore layout. impl.getElement().getStyle().setProperty("display", oldDisplay); } return asParticipant(t); } }; /** * Renderer for creating new parts of the DOM. Initially unset, then set once * in {@link #setRenderer(DomRenderer)}. */ private DomRenderer renderer; /** * Creates a view provider/manager/handler/oracle. */ public FullStructure() { } @Override public void setRenderer(DomRenderer renderer) { Preconditions.checkArgument(renderer != null); Preconditions.checkState(this.renderer == null); this.renderer = renderer; } /** @return the renderer for creating new sections of the DOM. */ private DomRenderer getRenderer() { Preconditions.checkState(renderer != null, "Renderer not ready"); return renderer; } /** * Narrows a view to the most specific common supertype of all views in this * package. * * @return {@code} v as a {@link DomView}. */ @SuppressWarnings("unchecked") private AbstractStructuredView<?, ? extends DomView> narrow(View v) { // Note: // Since the view APIs only exposes closed-universe synthesis of view // objects, this cast is safe. This could alternatively be implemented by // saving all synthesized views in a map of View -> DomView, and // implementing this via a lookup. The existence of a non-cast // implementation shows that narrowing is not the same as down-casting. The // use of down-casting here is just an optimization, and is not logically // unsound like regular down-casting is. return (AbstractStructuredView<?, ? extends DomView>) v; } /** @return the DOM element of a view. */ private Element elementOf(View v) { return v == null ? null : narrow(v).getIntrinsic().getElement(); } // // Adapters. // /** * Adapts a DOM element to the view implementation from this package. The view * implementation is chosen based on the element's kind attribute. */ private View viewOf(Element e) { if (e == null) { return null; } switch (typeOf(e)) { case ANCHOR: return asAnchorUnchecked(e); case BLIP: return asBlipUnchecked(e); case META: return asBlipMetaUnchecked(e); case MENU_ITEM: return asBlipMenuItemUnchecked(e); case INLINE_THREAD: return asInlineThreadUnchecked(e); case ROOT_THREAD: return asRootThreadUnchecked(e); case REPLY_BOX: return asRootThreadIndicatorUnchecked(e); case CONTINUATION_INDICATOR: return asContinuationIndicatorUnchecked(e); case ROOT_CONVERSATION: return asTopConversationUnchecked(e); case INLINE_CONVERSATION: return asInlineConversationUnchecked(e); case PARTICIPANT: return asParticipant(e); case PARTICIPANTS: return asParticipants(e); default: throw new AssertionError(); } } /** * @return true if {@code e} has a kind supported by a view implementation * from this package. */ private boolean hasKnownType(Element e) { return e.hasAttribute(KIND_ATTRIBUTE) && KNOWN_KINDS.contains(e.getAttribute(KIND_ATTRIBUTE)); } /** @return the view type of an element. */ private Type typeOf(Element e) { Type type = e.hasAttribute(KIND_ATTRIBUTE) ? type(e.getAttribute(KIND_ATTRIBUTE)) : null; if (type == null) { throw new RuntimeException("element has no known kind: " + e.getAttribute(KIND_ATTRIBUTE)); } else { return type; } } private RootThreadViewImpl<RootThreadDomImpl> asRootThreadUnchecked(Element e) { return e != null ? new RootThreadViewImpl<RootThreadDomImpl>( rootThreadHelper, RootThreadDomImpl.of(e)) : null; } private ReplyBoxViewImpl<ReplyBoxDomImpl> asRootThreadIndicatorUnchecked(Element e) { return e != null ? new ReplyBoxViewImpl<ReplyBoxDomImpl>( rootThreadIndicatorHelper, ReplyBoxDomImpl.of(e)) : null; } private InlineThreadViewImpl<InlineThreadDomImpl> asInlineThreadUnchecked(Element e) { return e != null ? new InlineThreadViewImpl<InlineThreadDomImpl>( inlineThreadHelper, InlineThreadDomImpl.of(e)) : null; } private ContinuationIndicatorViewImpl<ContinuationIndicatorDomImpl> asContinuationIndicatorUnchecked(Element e) { return e != null ? new ContinuationIndicatorViewImpl<ContinuationIndicatorDomImpl>( inlineThreadIndicatorHelper, ContinuationIndicatorDomImpl.of(e)) : null; } private AnchorViewImpl<AnchorDomImpl> asAnchorUnchecked(Element e) { return e != null ? new AnchorViewImpl<AnchorDomImpl>(anchorHelper, AnchorDomImpl.of(e)) : null; } private BlipViewImpl<BlipViewDomImpl> asBlipUnchecked(Element e) { return e != null ? new BlipViewImpl<BlipViewDomImpl>(blipHelper, BlipViewDomImpl.of(e)) : null; } private ParticipantViewImpl<ParticipantNameDomImpl> asParticipantUnchecked(Element e) { return e != null ? new ParticipantViewImpl<ParticipantNameDomImpl>( participantHelper, ParticipantNameDomImpl.of(e)) : null; } private ParticipantsViewImpl<ParticipantsDomImpl> asParticipantsUnchecked(Element e) { return e != null ? new ParticipantsViewImpl<ParticipantsDomImpl>( participantsHelper, ParticipantsDomImpl.of(e)) : null; } private BlipMetaViewImpl<BlipMetaDomImpl> asBlipMetaUnchecked(Element e) { return e != null ? new BlipMetaViewImpl<BlipMetaDomImpl>(metaHelper, BlipMetaDomImpl.of(e)) : null; } private BlipMenuItemViewImpl<BlipMenuItemDomImpl> asBlipMenuItemUnchecked(Element e) { return e != null ? BlipMenuItemViewImpl.create(menuHelper, BlipMenuItemDomImpl.of(e)) : null; } private TopConversationViewImpl<TopConversationDomImpl> asTopConversationUnchecked(Element e) { return e != null ? new TopConversationViewImpl<TopConversationDomImpl>( convHelper, TopConversationDomImpl.of(e)) : null; } private InlineConversationViewImpl<InlineConversationDomImpl> asInlineConversationUnchecked( Element e) { return e != null ? new InlineConversationViewImpl<InlineConversationDomImpl>( inlineConvHelper, InlineConversationDomImpl.of(e)) : null; } @Override public RootThreadViewImpl<RootThreadDomImpl> asRootThread(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.ROOT_THREAD); return asRootThreadUnchecked(e); } @Override public ReplyBoxViewImpl<ReplyBoxDomImpl> asReplyBox(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.REPLY_BOX); return asRootThreadIndicatorUnchecked(e); } @Override public InlineThreadViewImpl<InlineThreadDomImpl> asInlineThread(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.INLINE_THREAD); return asInlineThreadUnchecked(e); } @Override public ContinuationIndicatorView asContinuationIndicator(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.CONTINUATION_INDICATOR); return asContinuationIndicatorUnchecked(e); } @Override public BlipViewImpl<BlipViewDomImpl> asBlip(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.BLIP); return asBlipUnchecked(e); } @Override public ParticipantView asParticipant(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.PARTICIPANT); return asParticipantUnchecked(e); } @Override public ParticipantsView asParticipants(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.PARTICIPANTS); return asParticipantsUnchecked(e); } @Override public BlipMetaViewImpl<BlipMetaDomImpl> asBlipMeta(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.META); return asBlipMetaUnchecked(e); } @Override public BlipMenuItemViewImpl<BlipMenuItemDomImpl> asBlipMenuItem(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.MENU_ITEM); return asBlipMenuItemUnchecked(e); } @Override public AnchorViewImpl<AnchorDomImpl> asAnchor(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.ANCHOR); return asAnchorUnchecked(e); } @Override public TopConversationViewImpl<TopConversationDomImpl> asTopConversation(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.ROOT_CONVERSATION); return asTopConversationUnchecked(e); } @Override public InlineConversationViewImpl<InlineConversationDomImpl> asInlineConversation(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.INLINE_CONVERSATION); return asInlineConversationUnchecked(e); } @Override public AbstractConversationViewImpl<?, ?> asConversation(Element e) { if (e == null) { return null; } else { View.Type type = typeOf(e); switch (type) { case ROOT_CONVERSATION: return asTopConversationUnchecked(e); case INLINE_CONVERSATION: return asInlineConversationUnchecked(e); default: throw new IllegalArgumentException("Element has a non-conversation type: " + type); } } } @Override public InlineThreadView fromToggle(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.TOGGLE); return e != null ? new InlineThreadViewImpl<InlineThreadDomImpl>( inlineThreadHelper, InlineThreadDomImpl.ofToggle(e)) : null; } @Override public ParticipantsView fromAddButton(Element e) { Preconditions.checkArgument(e == null || typeOf(e) == Type.ADD_PARTICIPANT); while (e != null && !hasKnownType(e)) { e = e.getParentElement(); } // Assume that the nearest kinded ancestor of the add button is the // participants view (an exception is thrown if not). return asParticipants(e); } private AnchorView asAnchor(View v) { Preconditions.checkArgument(v == null || v.getType() == Type.ANCHOR); return (AnchorView) v; } private BlipView asBlip(View v) { Preconditions.checkArgument(v == null || v.getType() == Type.BLIP); return (BlipView) v; } private BlipMetaView asBlipMeta(View v) { Preconditions.checkArgument(v == null || v.getType() == Type.META); return (BlipMetaView) v; } private ThreadView asThread(View v) { Preconditions.checkArgument( v == null || v.getType() == Type.INLINE_THREAD || v.getType() == Type.ROOT_THREAD); return (ThreadView) v; } private RootThreadView asRootThread(View v) { Preconditions.checkArgument(v == null || v.getType() == Type.ROOT_THREAD); return (RootThreadView) v; } private InlineThreadView asInlineThread(View v) { Preconditions.checkArgument(v == null || v.getType() == Type.INLINE_THREAD); return (InlineThreadView) v; } private ConversationView asConversation(View v) { Preconditions.checkArgument(v == null || v.getType() == Type.ROOT_CONVERSATION || v.getType() == Type.INLINE_CONVERSATION); return (ConversationView) v; } // // Shared structure. // private View parentOf(DomView v) { Element c = v.getElement().getParentElement(); while (c != null && !hasKnownType(c)) { c = c.getParentElement(); } return viewOf(c); } }