Fixes WAVE-417 Adds GadgetStateChange event generation for robot API.
Project: http://git-wip-us.apache.org/repos/asf/incubator-wave/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-wave/commit/c2784ad2 Tree: http://git-wip-us.apache.org/repos/asf/incubator-wave/tree/c2784ad2 Diff: http://git-wip-us.apache.org/repos/asf/incubator-wave/diff/c2784ad2 Branch: refs/heads/master Commit: c2784ad28bdaa46c6224b0341e8f595f91fd7d27 Parents: 4499ce5 Author: Antonio Tenorio-Fornés <[email protected]> Authored: Sun Apr 12 23:37:34 2015 +0300 Committer: Yuri Zelikov <[email protected]> Committed: Sun Apr 12 23:39:19 2015 +0300 ---------------------------------------------------------------------- build.properties | 2 +- .../server/robots/passive/EventGenerator.java | 132 ++++++++++++++----- 2 files changed, 99 insertions(+), 35 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/c2784ad2/build.properties ---------------------------------------------------------------------- diff --git a/build.properties b/build.properties index 7f98247..86aad10 100644 --- a/build.properties +++ b/build.properties @@ -16,7 +16,7 @@ # under the License. # Current versions -waveinabox.version=0.4.0 +waveinabox.version=0.6.0 # Names name=wave-in-a-box http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/c2784ad2/src/org/waveprotocol/box/server/robots/passive/EventGenerator.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/passive/EventGenerator.java b/src/org/waveprotocol/box/server/robots/passive/EventGenerator.java index 206a33b..b45a0f8 100644 --- a/src/org/waveprotocol/box/server/robots/passive/EventGenerator.java +++ b/src/org/waveprotocol/box/server/robots/passive/EventGenerator.java @@ -23,56 +23,43 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.wave.api.BlipData; import com.google.wave.api.Context; +import com.google.wave.api.Gadget; import com.google.wave.api.data.converter.ContextResolver; import com.google.wave.api.data.converter.EventDataConverter; -import com.google.wave.api.event.AnnotatedTextChangedEvent; -import com.google.wave.api.event.DocumentChangedEvent; -import com.google.wave.api.event.Event; -import com.google.wave.api.event.EventType; -import com.google.wave.api.event.FormButtonClickedEvent; -import com.google.wave.api.event.WaveletBlipCreatedEvent; -import com.google.wave.api.event.WaveletBlipRemovedEvent; -import com.google.wave.api.event.WaveletParticipantsChangedEvent; -import com.google.wave.api.event.WaveletSelfAddedEvent; -import com.google.wave.api.event.WaveletSelfRemovedEvent; +import com.google.wave.api.event.*; import com.google.wave.api.impl.EventMessageBundle; import com.google.wave.api.robot.Capability; import com.google.wave.api.robot.RobotName; - import org.waveprotocol.box.server.robots.util.ConversationUtil; import org.waveprotocol.box.server.util.WaveletDataUtil; -import org.waveprotocol.wave.model.conversation.Conversation; -import org.waveprotocol.wave.model.conversation.ConversationBlip; -import org.waveprotocol.wave.model.conversation.ConversationListenerImpl; -import org.waveprotocol.wave.model.conversation.ObservableConversation; -import org.waveprotocol.wave.model.conversation.ObservableConversationBlip; -import org.waveprotocol.wave.model.conversation.WaveletBasedConversation; -import org.waveprotocol.wave.model.document.DocHandler; -import org.waveprotocol.wave.model.document.ObservableDocument; +import org.waveprotocol.wave.model.conversation.*; import org.waveprotocol.wave.model.document.Doc.E; import org.waveprotocol.wave.model.document.Doc.N; import org.waveprotocol.wave.model.document.Doc.T; +import org.waveprotocol.wave.model.document.DocHandler; +import org.waveprotocol.wave.model.document.ObservableDocument; import org.waveprotocol.wave.model.document.indexed.DocumentEvent; import org.waveprotocol.wave.model.document.indexed.DocumentEvent.AnnotationChanged; +import org.waveprotocol.wave.model.document.indexed.DocumentEvent.AttributesModified; import org.waveprotocol.wave.model.document.indexed.DocumentEvent.ContentInserted; -import org.waveprotocol.wave.model.document.raw.impl.Element; +import org.waveprotocol.wave.model.document.raw.impl.Node; import org.waveprotocol.wave.model.operation.OperationException; import org.waveprotocol.wave.model.operation.SilentOperationSink; import org.waveprotocol.wave.model.operation.wave.BasicWaveletOperationContextFactory; import org.waveprotocol.wave.model.operation.wave.TransformedWaveletDelta; import org.waveprotocol.wave.model.operation.wave.WaveletBlipOperation; import org.waveprotocol.wave.model.operation.wave.WaveletOperation; -import org.waveprotocol.wave.model.wave.ObservableWavelet; -import org.waveprotocol.wave.model.wave.ParticipantId; -import org.waveprotocol.wave.model.wave.ParticipationHelper; -import org.waveprotocol.wave.model.wave.WaveletListener; +import org.waveprotocol.wave.model.wave.*; import org.waveprotocol.wave.model.wave.data.ObservableWaveletData; import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet; import org.waveprotocol.wave.model.wave.opbased.WaveletListenerImpl; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * Generates Robot API Events from operations applied to a Wavelet. @@ -87,7 +74,7 @@ import java.util.Map; * <li>DocumentChanged (DONE)</li> * <li>AnnotatedTextChanged (DONE)</li> * <li>FormButtonClicked (TBD)</li> - * <li>GadgetStateChanged (TBD)</li> + * <li>GadgetStateChanged (DONE)</li> * <li>BlipContributorChanged (TBD)</li> * <li>WaveletTagsChanged (TBD)</li> * <li>WaveletTitleChanged (TBD)</li> @@ -258,13 +245,19 @@ public class EventGenerator { */ private boolean documentChangedEventGenerated; + private EventDataConverter converter; + private Wavelet wavelet; + public EventGeneratingDocumentHandler(ObservableDocument doc, ConversationBlip blip, Map<EventType, Capability> capabilities, EventMessageBundle messages, - ParticipantId deltaAuthor, Long deltaTimestamp) { + ParticipantId deltaAuthor, Long deltaTimestamp, Wavelet wavelet, + EventDataConverter converter) { this.doc = doc; this.blip = blip; this.capabilities = capabilities; this.messages = messages; + this.converter = converter; + this.wavelet = wavelet; setAuthorAndTimeStamp(deltaAuthor, deltaTimestamp); } @@ -283,10 +276,64 @@ public class EventGenerator { addEvent(apiEvent, capabilities, blip.getId(), messages); } } else { + // used to distinguish between attribute changes and gadget state + // changes + Boolean gadgetStateChangeEvent = false; + if (eventComponent.getType() == DocumentEvent.Type.ATTRIBUTES) { + if (capabilities.containsKey(EventType.GADGET_STATE_CHANGED)) { + Map<String, String> oldState = new HashMap<>(); + Integer index = -1; + try { + AttributesModified<N, E, T> attributesModified = + (AttributesModified<N, E, T>) eventComponent; + // When a gadget state changes, the AttributesModifies event has + // always + // an oldValue map of the form {"value", something} (key is + // always value). + // To obtain the key of the changed state, the attribute "name" + // has to be obtained + // from the Element of the AttributesModified event. + String name = + ((org.waveprotocol.wave.model.document.raw.impl.Element) attributesModified + .getElement()).getAttribute("name"); + String oldValue = attributesModified.getOldValues().get("value"); + if (name != null || oldValue != null) { + oldState.put(name, oldValue); + } + BlipData b = converter.toBlipData(blip, wavelet, messages); + Map<Integer, com.google.wave.api.Element> elements = b.getElements(); + Set<Integer> keys = elements.keySet(); + // The gadget element provided by the eventComponent + org.waveprotocol.wave.model.document.raw.impl.Element rawGadget = + ((Node) attributesModified.getElement()).getParentElement(); + for (Integer key : keys) { + try { + Gadget gadget = (Gadget) elements.get(key); + if (sameGadgets(rawGadget, gadget)) { + index = key; + break; + } + } catch (ClassCastException e) { + // if it is not a gadget we do not compare them + } + } + } catch (ClassCastException e) { + e.printStackTrace(); + } + if (oldState.size() != 0 && index != -1) { + // if the attribute changed belongs to a gadget + gadgetStateChangeEvent = true; + final GadgetStateChangedEvent gadgetEvent = + new GadgetStateChangedEvent(null, messages, deltaAuthor.getAddress(), + deltaTimestamp, blip.getId(), index, oldState); + addEvent(gadgetEvent, capabilities, blip.getId(), messages); + } + } + } if (capabilities.containsKey(EventType.FORM_BUTTON_CLICKED)) { if (eventComponent.getType() == DocumentEvent.Type.CONTENT_INSERTED) { ContentInserted<N, E, T> contentInserted = (ContentInserted<N, E, T>) eventComponent; - Element elementInserted = ((Element) contentInserted.getSubtreeElement()); + org.waveprotocol.wave.model.document.raw.impl.Element elementInserted = ((org.waveprotocol.wave.model.document.raw.impl.Element) contentInserted.getSubtreeElement()); if (elementInserted.getTagName().equals("click")) { FormButtonClickedEvent buttonClickedEvent = new FormButtonClickedEvent(null, null, @@ -299,9 +346,10 @@ public class EventGenerator { } else if (capabilities.containsKey(EventType.DOCUMENT_CHANGED) - && !documentChangedEventGenerated) { - DocumentChangedEvent apiEvent = new DocumentChangedEvent( - null, null, deltaAuthor.getAddress(), deltaTimestamp, blip.getId()); + && !documentChangedEventGenerated && !gadgetStateChangeEvent) { + DocumentChangedEvent apiEvent = + new DocumentChangedEvent(null, null, deltaAuthor.getAddress(), deltaTimestamp, + blip.getId()); addEvent(apiEvent, capabilities, blip.getId(), messages); // Only one documentChangedEvent should be generated per bundle. documentChangedEventGenerated = true; @@ -324,6 +372,21 @@ public class EventGenerator { this.deltaAuthor = author; this.deltaTimestamp = timestamp; } + + /** + * Check if an {@link org.waveprotocol.wave.model.document.raw.impl.Element} + * is and a {@link Gadget} + * + * @param rawElement + * @param element + * @return + */ + private boolean sameGadgets(org.waveprotocol.wave.model.document.raw.impl.Element rawElement, + Gadget element) { + String ifr1 = rawElement.getAttribute("ifr"); + String ifr2 = element.getProperty("ifr"); + return (ifr1 != null && ifr1.equals(ifr2)); + } } /** @@ -473,7 +536,7 @@ public class EventGenerator { // Check if we need to attach a doc handler. if ((op instanceof WaveletBlipOperation)) { attachDocHandler(conversation, op, docHandlers, capabilities, messages, - delta.getAuthor(), timestamp); + delta.getAuthor(), timestamp, wavelet, converter); } op.apply(snapshot); } @@ -515,7 +578,7 @@ public class EventGenerator { private void attachDocHandler(ObservableConversation conversation, WaveletOperation op, Map<String, EventGeneratingDocumentHandler> docHandlers, Map<EventType, Capability> capabilities, EventMessageBundle messages, - ParticipantId deltaAuthor, long timestamp) { + ParticipantId deltaAuthor, long timestamp, Wavelet wavelet, EventDataConverter converter) { WaveletBlipOperation blipOp = (WaveletBlipOperation) op; String blipId = blipOp.getBlipId(); // Ignoring the documents outside the conversation such as tags @@ -527,8 +590,9 @@ public class EventGenerator { EventGeneratingDocumentHandler docHandler = docHandlers.get(blipId1); if (docHandler == null) { ObservableDocument doc = (ObservableDocument) blip.getContent(); - docHandler = new EventGeneratingDocumentHandler( - doc, blip, capabilities, messages, deltaAuthor, timestamp); + docHandler = + new EventGeneratingDocumentHandler(doc, blip, capabilities, messages, deltaAuthor, + timestamp, wavelet, converter); doc.addListener(docHandler); docHandlers.put(blipId1, docHandler); } else {
