http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/com/google/wave/api/AbstractRobot.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/com/google/wave/api/AbstractRobot.java b/wave/src/main/java/com/google/wave/api/AbstractRobot.java deleted file mode 100644 index 435eab1..0000000 --- a/wave/src/main/java/com/google/wave/api/AbstractRobot.java +++ /dev/null @@ -1,919 +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 com.google.wave.api; - -import com.google.gson.Gson; -import com.google.wave.api.WaveService.ConsumerData; -import com.google.wave.api.event.AnnotatedTextChangedEvent; -import com.google.wave.api.event.BlipContributorsChangedEvent; -import com.google.wave.api.event.BlipSubmittedEvent; -import com.google.wave.api.event.DocumentChangedEvent; -import com.google.wave.api.event.Event; -import com.google.wave.api.event.EventHandler; -import com.google.wave.api.event.EventType; -import com.google.wave.api.event.FormButtonClickedEvent; -import com.google.wave.api.event.GadgetStateChangedEvent; -import com.google.wave.api.event.OperationErrorEvent; -import com.google.wave.api.event.WaveletBlipCreatedEvent; -import com.google.wave.api.event.WaveletBlipRemovedEvent; -import com.google.wave.api.event.WaveletCreatedEvent; -import com.google.wave.api.event.WaveletFetchedEvent; -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.WaveletTagsChangedEvent; -import com.google.wave.api.event.WaveletTitleChangedEvent; -import com.google.wave.api.impl.EventMessageBundle; -import com.google.wave.api.impl.GsonFactory; - -import net.oauth.OAuthException; - -import org.waveprotocol.wave.model.id.InvalidIdException; -import org.waveprotocol.wave.model.id.WaveId; -import org.waveprotocol.wave.model.id.WaveletId; - -import java.io.BufferedReader; -import java.io.IOException; -import java.lang.reflect.Method; -import java.net.HttpURLConnection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.logging.Logger; - -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * A robot is an automated participant on a wave, that can read the contents - * of a wave in which it participates, modify the wave's contents, add or remove - * participants, and create new blips and new waves. In short, a robot can - * perform many of the actions that any other participant can perform. - * - * This is the abstract base class for a Google Wave Java robot, that supports: - * <ul> - * <li>Automatic events deserialization and operations serialization, - * in the event based model</li> - * <li>OAuth-secured operations submission, in the active model</li> - * <li>Callback for profile request, including proxied/custom profile</li> - * <li>Callback for capabilities.xml support</li> - * <li>Callback for verification token request, that is used during the robot - * registration process, to obtain consumer key and secret</li> - * </ul> - * - * Robot should implements the handlers of the events that it's interested in, - * and specify the context and filter (if applicable) via the - * {@link EventHandler.Capability} annotation. For example, if it is interested - * in a {@link BlipSubmittedEvent}, and would like to get the parent blip with - * the incoming event bundle, then it should implement this method: - * <pre> - * @Capability(contexts = {Context.PARENT, Context.SELF}) - * public void onBlipSubmitted(BlipSubmittedEvent e) { - * ... - * } - * </pre> - * If the robot does not specify the {@link EventHandler.Capability} - * annotation, the default contexts (parent and children), and empty filter will - * be provided by default. - */ -public abstract class AbstractRobot extends HttpServlet implements EventHandler { - - /** Some mime types. */ - public static final String JSON_MIME_TYPE = "application/json; charset=utf-8"; - public static final String TEXT_MIME_TYPE = "text/plain"; - public static final String XML_MIME_TYPE = "application/xml"; - - /** Some constants for encoding. */ - public static final String UTF_8 = "UTF-8"; - - /** The query parameter to specify custom profile request. */ - public static final String NAME_QUERY_PARAMETER_KEY = "name"; - - /** The query parameter for security token. */ - public static final String SECURITY_TOKEN_PARAMETER_KEY = "st"; - - /** Various request path constants that the robot replies to. */ - public static final String RPC_PATH = "/_wave/robot/jsonrpc"; - public static final String PROFILE_PATH = "/_wave/robot/profile"; - public static final String CAPABILITIES_PATH = "/_wave/capabilities.xml"; - public static final String VERIFY_TOKEN_PATH = "/_wave/verify_token"; - public static final String DEFAULT_AVATAR = - "https://wave.google.com/a/wavesandbox.com/static/images/profiles/rusty.png"; - - private static final Logger LOG = Logger.getLogger(AbstractRobot.class.getName()); - - /** Serializer to serialize events and operations in the event-based mode. */ - private static final Gson SERIALIZER = new GsonFactory().create(); - - /** A map of this robot's capabilities. */ - private Map<String, Capability> capabilityMap; - - /** A version number that is computed from this robot's capabilities. */ - private String version; - - /** A utility class to make Wave service calls. */ - private WaveService waveService; - - /** The token used to verify author during the registration process. */ - private String verificationToken; - - /** The token that is checked when handling verification token request. */ - private String securityToken; - - private boolean allowUnsignedRequests = true; - - /** - * Constructor. - */ - protected AbstractRobot() { - initRobot(); - } - - /** - * Initializes the robot. Call it if required to re-compute robot's - * capabilities. Re-invoke {@link #setupOAuth} if needed. - */ - protected void initRobot() { - capabilityMap = computeCapabilityMap(); - version = computeHash(); - waveService = new WaveService(version); - } - - /** - * Submits the pending operations associated with this {@link Wavelet}. - * - * @param wavelet the bundle that contains the operations to be submitted. - * @param rpcServerUrl the active gateway to send the operations to. - * @return a list of {@link JsonRpcResponse} that represents the responses - * from the server for all operations that were submitted. - * - * @throws IllegalStateException if this method is called prior to setting - * the proper consumer key, secret, and handler URL. - * @throws IOException if there is a problem submitting the operations. - */ - public List<JsonRpcResponse> submit(Wavelet wavelet, String rpcServerUrl) throws IOException { - return waveService.submit(wavelet, rpcServerUrl); - } - - /** - * Returns an empty/blind stub of a wavelet with the given wave id and wavelet - * id. - * - * Call this method if you would like to apply wavelet-only operations - * without fetching the wave first. - * - * The returned wavelet has its own {@link OperationQueue}. It is the - * responsibility of the caller to make sure this wavelet gets submitted to - * the server, either by calling {@link AbstractRobot#submit(Wavelet, String)} - * or by calling {@link Wavelet#submitWith(Wavelet)} on the new wavelet, to - * join its queue with another wavelet, for example, the event wavelet. - * - * @param waveId the id of the wave. - * @param waveletId the id of the wavelet. - * @return a stub of a wavelet. - */ - public Wavelet blindWavelet(WaveId waveId, WaveletId waveletId) { - return waveService.blindWavelet(waveId, waveletId); - } - - /** - * @see #blindWavelet(WaveId, WaveletId) - * - * @param proxyForId the proxying information that should be set on the - * operation queue. Please note that this parameter should be properly - * encoded to ensure that the resulting participant id is valid - * (see {@link Util#checkIsValidProxyForId(String)} for more details). - */ - public Wavelet blindWavelet(WaveId waveId, WaveletId waveletId, String proxyForId) { - return waveService.blindWavelet(waveId, waveletId, proxyForId); - } - - /** - * @see #blindWavelet(WaveId, WaveletId, String) - * - * @param blips a collection of blips that belong to this wavelet. - */ - public Wavelet blindWavelet(WaveId waveId, WaveletId waveletId, String proxyForId, - Map<String, Blip> blips) { - return waveService.blindWavelet(waveId, waveletId, proxyForId, blips); - } - - /** - * @see #blindWavelet(WaveId, WaveletId, String, Map) - * - * @param threads a collection of threads that belong to this wavelet. - */ - public Wavelet blindWavelet(WaveId waveId, WaveletId waveletId, String proxyForId, - Map<String, Blip> blips, Map<String, BlipThread> threads) { - return waveService.blindWavelet(waveId, waveletId, proxyForId, blips, threads); - } - - /** - * Creates a new wave with a list of participants on it. - * - * The root wavelet of the new wave is returned with its own - * {@link OperationQueue}. It is the responsibility of the caller to make sure - * this wavelet gets submitted to the server, either by calling - * {@link AbstractRobot#submit(Wavelet, String)} or by calling - * {@link Wavelet#submitWith(Wavelet)} on the new wavelet. - * - * @param domain the domain to create the wavelet on. In general, this should - * correspond to the domain of the incoming event wavelet, except when - * the robot is calling this method outside of an event or when the server - * is handling multiple domains. - * @param participants the initial participants on the wave. The robot, as the - * creator of the wave, will be added by default. The order of the - * participants will be preserved. - */ - public Wavelet newWave(String domain, Set<String> participants) { - return waveService.newWave(domain, participants); - } - - /** - * @see #newWave(String, Set) - * - * @param proxyForId the proxy id that should be used to create the new wave. - * If specified, the creator of the wave would be - * robotid+<proxyForId>@appspot.com. Please note that this parameter - * should be properly encoded to ensure that the resulting participant id - * is valid (see {@link Util#checkIsValidProxyForId(String)} for more - * details). - */ - public Wavelet newWave(String domain, Set<String> participants, String proxyForId) { - return waveService.newWave(domain, participants, proxyForId); - } - - /** - * @see #newWave(String, Set, String) - * - * @param msg the message that will be passed back to the robot when - * WAVELET_CREATED event is fired as a result of this operation. - */ - public Wavelet newWave(String domain, Set<String> participants, String msg, String proxyForId) { - return waveService.newWave(domain, participants, msg, proxyForId); - } - - /** - * @see #newWave(String, Set, String, String) - * - * @param rpcServerUrl if specified, this operation will be submitted - * immediately to this active gateway, that will return immediately the - * actual wave id, the id of the root wavelet, and id of the root blip. - * - * @throws IOException if there is a problem submitting the operation to the - * server, when {@code submit} is {@code true}. - * @throws InvalidIdException - */ - public Wavelet newWave(String domain, Set<String> participants, String msg, String proxyForId, - String rpcServerUrl) throws IOException, InvalidIdException { - return waveService.newWave(domain, participants, msg, proxyForId, rpcServerUrl); - } - - - /** - * Requests SearchResult for a query. - * - * @param query the query to execute. - * @param index the index from which to return results. - * @param numresults the number of results to return. - * @param rpcServerUrl the active gateway. - * - * @throws IOException if remote server returns error. - */ - public SearchResult search(String query, Integer index, Integer numResults, String rpcServerUrl) - throws IOException { - SearchResult searchResult = waveService.search(query, index, numResults, rpcServerUrl); - return searchResult; - } - - /** - * Fetches a wavelet using the active API. - * - * The returned wavelet contains a snapshot of the state of the wavelet at - * that point. It can be used to modify the wavelet, but the wavelet might - * change in between, so treat carefully. - * - * Also, the returned wavelet has its own {@link OperationQueue}. It is the - * responsibility of the caller to make sure this wavelet gets submitted to - * the server, either by calling {@link AbstractRobot#submit(Wavelet, String)} - * or by calling {@link Wavelet#submitWith(Wavelet)} on the new wavelet. - * - * @param waveId the id of the wave to fetch. - * @param waveletId the id of the wavelet to fetch. - * @param rpcServerUrl the active gateway that is used to fetch the wavelet. - * - * @throws IOException if there is a problem fetching the wavelet. - */ - public Wavelet fetchWavelet(WaveId waveId, WaveletId waveletId, String rpcServerUrl) - throws IOException { - return waveService.fetchWavelet(waveId, waveletId, rpcServerUrl); - } - - /** - * @see #fetchWavelet(WaveId, WaveletId, String) - * - * @param proxyForId the proxy id that should be used to fetch this wavelet. - * Please note that this parameter should be properly encoded to ensure - * that the resulting participant id is valid (see - * {@link Util#checkIsValidProxyForId(String)} for more details). - */ - public Wavelet fetchWavelet(WaveId waveId, WaveletId waveletId, String proxyForId, - String rpcServerUrl) throws IOException { - return waveService.fetchWavelet(waveId, waveletId, proxyForId, rpcServerUrl); - } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) { - if (req.getRequestURI().endsWith(RPC_PATH)) { - processRpc(req, resp); - } else { - resp.setStatus(HttpURLConnection.HTTP_NOT_FOUND); - } - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) { - String path = req.getRequestURI(); - if (path.endsWith(PROFILE_PATH)) { - processProfile(req, resp); - } else if (path.endsWith(CAPABILITIES_PATH)) { - processCapabilities(req, resp); - } else if (path.endsWith(VERIFY_TOKEN_PATH)) { - processVerifyToken(req, resp); - } else { - resp.setStatus(HttpURLConnection.HTTP_NOT_FOUND); - } - } - - /** - * @return a custom profile based on "name" query parameter, or {@code null} - * if this robot doesn't support custom profile. - */ - protected ParticipantProfile getCustomProfile(@SuppressWarnings("unused") String name) { - return null; - } - - /** - * @return the URL of the robot's avatar image. - */ - protected String getRobotAvatarUrl() { - return DEFAULT_AVATAR; - } - - /** - * Sets up the verification token that is used for the owner verification - * step during the robot registration. - * - * @param verificationToken the verification token. - * @param securityToken the security token that should be matched against when - * serving a verification token request. - */ - protected void setupVerificationToken(String verificationToken, String securityToken) { - this.verificationToken = verificationToken; - this.securityToken = securityToken; - } - - /** - * Sets the OAuth related properties, including the consumer key and secret - * that are used to sign the outgoing operations in the active mode. Robot - * developer needs to visit - * {@link "http://wave.google.com/wave/robot/register"} to register and obtain - * the consumer key and secret. - * - * Should you need to make Active API calls to both our public and sandbox - * servers from the same robot, you can call this method multiple times, with - * the same consumer key and secret, but different RPC server URLs. - * - * After calling this method, the robot no longer accepts unsigned requests, - * but you can override that by calling - * {@link #setAllowUnsignedRequests(boolean)}. - * - * @param consumerKey the consumer key. - * @param consumerSecret the consumer secret. - * @param rpcServerUrl the URL of the server that serves the JSON-RPC request. - * <ul> - * <li>https://www-opensocial.googleusercontent.com/api/rpc - for wave - * preview.<li> - * <li>https://www-opensocial-sandbox.googleusercontent.com/api/rpc - - * for wave sandbox.</li> - * </ul> - * - * @throws IllegalArgumentException if any of the arguments are {@code null}. - */ - protected void setupOAuth(String consumerKey, String consumerSecret, String rpcServerUrl) { - waveService.setupOAuth(consumerKey, consumerSecret, rpcServerUrl); - setAllowUnsignedRequests(false); - } - - /** - * Sets the OAuth related properties, including the consumer key and secret - * that are used to sign the outgoing operations in the active mode. This - * method sets the JSON-RPC handler URL to - * https://www-opensocial.googleusercontent.com/api/rpc, that is associated - * with wave preview instance. - * - * @param consumerKey the consumer key. - * @param consumerSecret the consumer secret. - */ - protected void setupOAuth(String consumerKey, String consumerSecret) { - setupOAuth(consumerKey, consumerSecret, WaveService.RPC_URL); - } - - /** - * Sets whether or not unsigned incoming requests from robot proxy are - * allowed. - * - * @param allowUnsignedRequests whether or not unsigned requests from robot - * proxy are allowed. - */ - protected void setAllowUnsignedRequests(boolean allowUnsignedRequests) { - if (!allowUnsignedRequests && waveService.getConsumerDataMap().isEmpty()) { - throw new IllegalArgumentException("Please call AbstractRobot.setupOAuth() first to " + - "setup the consumer key and secret to validate the request."); - } - this.allowUnsignedRequests = allowUnsignedRequests; - } - - /** - * @return {@code true} if unsigned incoming requests from robot proxy are - * allowed. - */ - protected boolean isUnsignedRequestsAllowed() { - return allowUnsignedRequests; - } - - /** - * Processes the incoming HTTP request to obtain the verification token. - * - * @param req the HTTP request. - * @param resp the HTTP response. - */ - private void processVerifyToken(HttpServletRequest req, HttpServletResponse resp) { - if (verificationToken == null || verificationToken.isEmpty()) { - LOG.info("Please register a verification token by calling " + - "AbstractRobot.setVerificationToken()."); - resp.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); - return; - } - - String incomingSecurityToken = req.getParameter(SECURITY_TOKEN_PARAMETER_KEY); - if (securityToken != null && !securityToken.equals(incomingSecurityToken)) { - LOG.info("The incoming security token " + incomingSecurityToken + " does not match the " + - "expected security token " + securityToken + "."); - resp.setStatus(HttpURLConnection.HTTP_UNAUTHORIZED); - return; - } - - resp.setContentType(TEXT_MIME_TYPE); - resp.setCharacterEncoding(UTF_8); - try { - resp.getWriter().write(verificationToken); - } catch (IOException e) { - resp.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); - return; - } - resp.setStatus(HttpURLConnection.HTTP_OK); - } - - /** - * Processes the incoming HTTP request to obtain the capabilities.xml file. - * - * @param req the HTTP request. - * @param resp the HTTP response. - */ - private void processCapabilities(HttpServletRequest req, HttpServletResponse resp) { - StringBuilder xml = new StringBuilder(); - xml.append("<?xml version=\"1.0\"?>\n"); - xml.append("<w:robot xmlns:w=\"http://wave.google.com/extensions/robots/1.0\">\n"); - xml.append(" <w:version>"); - xml.append(version); - xml.append("</w:version>\n"); - xml.append(" <w:protocolversion>"); - xml.append(WaveService.PROTOCOL_VERSION.getVersionString()); - xml.append("</w:protocolversion>\n"); - xml.append(" <w:capabilities>\n"); - for (Entry<String, Capability> entry : capabilityMap.entrySet()) { - xml.append(" <w:capability name=\"" + entry.getKey() + "\""); - Capability capability = entry.getValue(); - if (capability != null) { - // Append context. - if (capability.contexts().length != 0) { - xml.append(" context=\""); - boolean first = true; - for (Context context : capability.contexts()) { - if (first) { - first = false; - } else { - xml.append(','); - } - xml.append(context.name()); - } - xml.append("\""); - } - - // Append filter. - if (capability.filter() != null && !capability.filter().isEmpty()) { - xml.append(" filter=\""); - xml.append(capability.filter()); - xml.append("\""); - } - } - xml.append("/>\n"); - } - xml.append(" </w:capabilities>\n"); - if (!waveService.getConsumerDataMap().isEmpty()) { - xml.append(" <w:consumer_keys>\n"); - for (ConsumerData consumerDataObj : waveService.getConsumerDataMap().values()) { - xml.append(" <w:consumer_key for=\"" + consumerDataObj.getRpcServerUrl() + "\">" - + consumerDataObj.getConsumerKey() + "</w:consumer_key>\n"); - } - xml.append(" </w:consumer_keys>\n"); - } - xml.append("</w:robot>\n"); - // Write the result into the output stream. - resp.setContentType(XML_MIME_TYPE); - resp.setCharacterEncoding(UTF_8); - try { - resp.getWriter().write(xml.toString()); - } catch (IOException e) { - resp.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); - return; - } - resp.setStatus(HttpURLConnection.HTTP_OK); - } - - /** - * Processes the incoming HTTP request to obtain robot's profile. - * - * @param req the HTTP request. - * @param resp the HTTP response. - */ - private void processProfile(HttpServletRequest req, HttpServletResponse resp) { - ParticipantProfile profile = null; - - // Try to get custom profile. - String proxiedName = req.getParameter(NAME_QUERY_PARAMETER_KEY); - if (proxiedName != null) { - profile = getCustomProfile(proxiedName); - } - - // Set the default profile. - if (profile == null) { - profile = new ParticipantProfile(getRobotName(), getRobotAvatarUrl(), - getRobotProfilePageUrl()); - } - - // Write the result into the output stream. - resp.setContentType(JSON_MIME_TYPE); - resp.setCharacterEncoding(UTF_8); - try { - resp.getWriter().write(SERIALIZER.toJson(profile)); - } catch (IOException e) { - resp.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); - return; - } - resp.setStatus(HttpURLConnection.HTTP_OK); - } - - /** - * Processes the incoming HTTP request that contains the event bundle. - * - * @param req the HTTP request. - * @param resp the HTTP response. - */ - private void processRpc(HttpServletRequest req, HttpServletResponse resp) { - // Deserialize and process the incoming events. - EventMessageBundle events = null; - try { - events = deserializeEvents(req); - } catch (IOException e) { - resp.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); - return; - } - - // Append robot.notifyCapabilitiesHash operation before processing the - // events. - OperationQueue operationQueue = events.getWavelet().getOperationQueue(); - operationQueue.notifyRobotInformation(WaveService.PROTOCOL_VERSION, version); - - // Call the robot event handlers. - processEvents(events); - - // Serialize the operations. - serializeOperations(operationQueue.getPendingOperations(), resp); - operationQueue.clear(); - } - - /** - * Processes the incoming event bundle. This method iterates over the event - * bundle and dispatch the individual event to its own handler, based on the - * event type. - * - * @param events the incoming event bundle. - */ - protected void processEvents(EventMessageBundle events) { - for (Event event : events.getEvents()) { - switch (event.getType()) { - case ANNOTATED_TEXT_CHANGED: - onAnnotatedTextChanged(AnnotatedTextChangedEvent.as(event)); - break; - case BLIP_CONTRIBUTORS_CHANGED: - onBlipContributorsChanged(BlipContributorsChangedEvent.as(event)); - break; - case BLIP_SUBMITTED: - onBlipSubmitted(BlipSubmittedEvent.as(event)); - break; - case DOCUMENT_CHANGED: - onDocumentChanged(DocumentChangedEvent.as(event)); - break; - case FORM_BUTTON_CLICKED: - onFormButtonClicked(FormButtonClickedEvent.as(event)); - break; - case GADGET_STATE_CHANGED: - onGadgetStateChanged(GadgetStateChangedEvent.as(event)); - break; - case WAVELET_BLIP_CREATED: - onWaveletBlipCreated(WaveletBlipCreatedEvent.as(event)); - break; - case WAVELET_BLIP_REMOVED: - onWaveletBlipRemoved(WaveletBlipRemovedEvent.as(event)); - break; - case WAVELET_CREATED: - onWaveletCreated(WaveletCreatedEvent.as(event)); - break; - case WAVELET_FETCHED: - onWaveletFetched(WaveletFetchedEvent.as(event)); - break; - case WAVELET_PARTICIPANTS_CHANGED: - onWaveletParticipantsChanged(WaveletParticipantsChangedEvent.as(event)); - break; - case WAVELET_SELF_ADDED: - onWaveletSelfAdded(WaveletSelfAddedEvent.as(event)); - break; - case WAVELET_SELF_REMOVED: - onWaveletSelfRemoved(WaveletSelfRemovedEvent.as(event)); - break; - case WAVELET_TAGS_CHANGED: - onWaveletTagsChanged(WaveletTagsChangedEvent.as(event)); - break; - case WAVELET_TITLE_CHANGED: - onWaveletTitleChanged(WaveletTitleChangedEvent.as(event)); - break; - case OPERATION_ERROR: - onOperationError(OperationErrorEvent.as(event)); - break; - } - } - } - - /** - * Computes this robot's capabilities, based on the overriden event handler - * methods, and their {@link EventHandler.Capability} annotations. - * - * The result map does not use {@link EventType} enum as the key for stability - * between JVM runs, since the same enum may have different hashcode between - * JVM runs. This may cause two instances of the same robot that are running - * on different JVMs (for example, when App Engine scale the robot) to have - * different version number and capabilities ordering in - * {@code capabilities.xml}. - * - * @return a map of event type string to capability. - */ - protected Map<String, Capability> computeCapabilityMap() { - Map<String, Capability> map = new HashMap<String, Capability>(); - for (Method baseMethod : EventHandler.class.getDeclaredMethods()) { - Method overridenMethod = null; - try { - overridenMethod = this.getClass().getMethod(baseMethod.getName(), - baseMethod.getParameterTypes()); - } catch (NoSuchMethodException e) { - // Robot does not override this particular event handler. Continue. - continue; - } - - // Skip the method, if it's declared in AbstractRobot. - if (AbstractRobot.class.equals(overridenMethod.getDeclaringClass())) { - continue; - } - - // Get the event type. - EventType eventType = EventType.fromClass(overridenMethod.getParameterTypes()[0]); - - // Get the capability annotation. - Capability capability = overridenMethod.getAnnotation(Capability.class); - - map.put(eventType.toString(), capability); - } - return map; - } - - /** - * Computes this robot's hash, based on the capabilities. - * - * @return a hash of this robot, computed from it's capabilities. - */ - protected String computeHash() { - long version = 0l; - for (Entry<String, Capability> entry : capabilityMap.entrySet()) { - long hash = entry.getKey().hashCode(); - Capability capability = entry.getValue(); - if (capability != null) { - for (Context context : capability.contexts()) { - hash = hash * 31 + context.name().hashCode(); - } - hash = hash * 31 + capability.filter().hashCode(); - } - version = version * 17 + hash; - } - return Long.toHexString(version); - } - - /** - * Deserializes the given HTTP request's JSON body into an event message - * bundle. - * - * @param req the HTTP request to be deserialized. - * @return an event message bundle. - * - * @throws IOException if there is a problem reading the request's body. - * @throws IllegalArgumentException if the request is not signed properly. - */ - private EventMessageBundle deserializeEvents(HttpServletRequest req) throws IOException { - String json = readRequestBody(req); - LOG.info("Incoming events: " + json); - - EventMessageBundle bundle = SERIALIZER.fromJson(json, EventMessageBundle.class); - - if (bundle.getRpcServerUrl() == null) { - throw new IllegalArgumentException("RPC server URL is not set in the event bundle."); - } - - if (!isUnsignedRequestsAllowed()) { - if (!waveService.hasConsumerData(bundle.getRpcServerUrl())) { - throw new IllegalArgumentException("No consumer key is found for the RPC server URL: " + - bundle.getRpcServerUrl()); - } - - // Validates the request. - try { - @SuppressWarnings("unchecked") - Map<String, String[]> parameterMap = req.getParameterMap(); - waveService.validateOAuthRequest(req.getRequestURL().toString(), parameterMap, - json, bundle.getRpcServerUrl()); - } catch (OAuthException e) { - throw new IllegalArgumentException("Error validating OAuth request", e); - } - } - return bundle; - } - /** - * Serializes the given outgoing operations into a JSON string, and put it in - * the given response object. - * - * @param operations the operations to be serialized. - * @param resp the response object to flush the output string into. - */ - private static void serializeOperations(List<OperationRequest> operations, - HttpServletResponse resp) { - try { - String json = SERIALIZER.toJson(operations, GsonFactory.OPERATION_REQUEST_LIST_TYPE); - LOG.info("Outgoing operations: " + json); - - resp.setContentType(JSON_MIME_TYPE); - resp.setCharacterEncoding(UTF_8); - resp.getWriter().write(json); - resp.setStatus(HttpURLConnection.HTTP_OK); - } catch (IOException iox) { - resp.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); - } - } - - /** - * Reads the given HTTP request's input stream into a string. - * - * @param req the HTTP request to be read. - * @return a string representation of the given HTTP request's body. - * - * @throws IOException if there is a problem reading the body. - */ - private static String readRequestBody(HttpServletRequest req) throws IOException { - StringBuilder json = new StringBuilder(); - BufferedReader reader = req.getReader(); - String line; - while ((line = reader.readLine()) != null) { - json.append(line); - } - return json.toString(); - } - - @Override - public void onAnnotatedTextChanged(AnnotatedTextChangedEvent event) { - // No-op. - } - - @Override - public void onBlipContributorsChanged(BlipContributorsChangedEvent event) { - // No-op. - } - - @Override - public void onBlipSubmitted(BlipSubmittedEvent event) { - // No-op. - } - - @Override - public void onDocumentChanged(DocumentChangedEvent event) { - // No-op. - } - - @Override - public void onFormButtonClicked(FormButtonClickedEvent event) { - // No-op. - } - - @Override - public void onGadgetStateChanged(GadgetStateChangedEvent event) { - // No-op. - } - - @Override - public void onWaveletBlipCreated(WaveletBlipCreatedEvent event) { - // No-op. - } - - - @Override - public void onWaveletBlipRemoved(WaveletBlipRemovedEvent event) { - // No-op. - } - - @Override - public void onWaveletCreated(WaveletCreatedEvent event) { - // No-op. - } - - @Override - public void onWaveletFetched(WaveletFetchedEvent event) { - // No-op. - } - - @Override - public void onWaveletParticipantsChanged(WaveletParticipantsChangedEvent event) { - // No-op. - } - - @Override - public void onWaveletSelfAdded(WaveletSelfAddedEvent event) { - // No-op. - } - - @Override - public void onWaveletSelfRemoved(WaveletSelfRemovedEvent event) { - // No-op. - } - - @Override - public void onWaveletTagsChanged(WaveletTagsChangedEvent event) { - // No-op. - } - - @Override - public void onWaveletTitleChanged(WaveletTitleChangedEvent event) { - // No-op. - } - - @Override - public void onOperationError(OperationErrorEvent event) { - // No-op. - } - - /** - * @return the display name of the robot. - */ - protected abstract String getRobotName(); - - /** - * @return the URL of the robot's profile page. - */ - protected abstract String getRobotProfilePageUrl(); -}
http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/com/google/wave/api/Annotation.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/com/google/wave/api/Annotation.java b/wave/src/main/java/com/google/wave/api/Annotation.java deleted file mode 100644 index 5b137b2..0000000 --- a/wave/src/main/java/com/google/wave/api/Annotation.java +++ /dev/null @@ -1,145 +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 com.google.wave.api; - -import java.io.Serializable; - - -/* - * Annotation is an immutable key-value pair of metadata over a range of - * content. Annotation can be used to store data or to be interpreted by a - * client when displaying the data. - * - * Example uses of annotation include styling text, supplying spelling - * corrections, and links to refer that area of text to another document or - * web site. - */ -public class Annotation implements Serializable { - - /** Some constants for style related annotation keys. */ - public static final String BACKGROUND_COLOR = "style/backgroundColor"; - public static final String COLOR = "style/color"; - public static final String FONT_FAMILY = "style/fontFamily"; - public static final String FONT_SIZE = "style/fontSize"; - public static final String FONT_STYLE = "style/fontStyle"; - public static final String FONT_WEIGHT = "style/fontWeight"; - public static final String TEXT_DECORATION = "style/textDecoration"; - public static final String VERTICAL_ALIGN = "style/verticalAlign"; - public static final String LINK = "link/manual"; - public static final String WAVE_LINK = "link/wave"; - - /** The annotation name. */ - private final String name; - - /** The annotation value. */ - private final String value; - - /** The range of this annotation. */ - private Range range; - - /** - * Constructor. - * - * @param name the name of this annotation. - * @param value the value of this annotation. - * @param start the starting index of this annotation. - * @param end the end index of this annotation. - */ - public Annotation(String name, String value, int start, int end) { - this.name = name; - this.value = value; - this.range = new Range(start, end); - } - - /** - * Constructor. - * - * @param name the name of this annotation. - * @param value the value of this annotation. - * @param range the range of this annotation. - */ - public Annotation(String name, String value, Range range) { - this.name = name; - this.value = value; - this.range = new Range(range.getStart(), range.getEnd()); - } - - /** - * Returns the name of this annotation. - * - * @return the annotation's name. - */ - public String getName() { - return name; - } - - /** - * Returns the value of this annotation. - * - * @return the annotation's value. - */ - public String getValue() { - return value; - } - - /** - * Returns the range of this annotation. - * - * @return the annotation range. - */ - public Range getRange() { - return range; - } - - /** - * Shifts this annotation by {@code shiftAmount} if it is on a range that - * is after or covers the given position. - * - * @param position the anchor position. - * @param shiftAmount the amount to shift the annotation range. - */ - public void shift(int position, int shiftAmount) { - int start = range.getStart(); - if (start >= position) { - start += shiftAmount; - } - - int end = range.getEnd(); - if (end >= position) { - end += shiftAmount; - } - range = new Range(start, end); - } - - @Override - public String toString() { - StringBuilder res = new StringBuilder("Annotation("); - if (name != null) { - res.append(name); - res.append(','); - } - if (value != null) { - res.append(value); - res.append(','); - } - res.append(range.toString()); - return res.toString(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/com/google/wave/api/Annotations.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/com/google/wave/api/Annotations.java b/wave/src/main/java/com/google/wave/api/Annotations.java deleted file mode 100644 index dbecd3e..0000000 --- a/wave/src/main/java/com/google/wave/api/Annotations.java +++ /dev/null @@ -1,265 +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 com.google.wave.api; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; - -/** - * A class that models a map of annotations, keyed by the annotation name. - * - * Each key maps into a list of {@link Annotation} instances, since one - * annotation name can exist in different parts (denoted by different ranges) of - * a blip, each with its own value. - * - * This class is iterable, but the iterator does not support element removal - * yet. - */ -public class Annotations implements Iterable<Annotation>, Serializable { - - /** A map of annotation name to a list of annotations instances. */ - private final Map<String, List<Annotation>> store = new HashMap<String, List<Annotation>>(); - - /** The total number of annotations. */ - private int size; - - /** - * Adds a new annotation. - * - * @param name the name of the annotation. - * @param value the value of the annotation. - * @param start the starting index of the annotation. - * @param end the end index of the annotation. - */ - void add(String name, String value, int start, int end) { - if (!store.containsKey(name)) { - store.put(name, Arrays.asList(new Annotation(name, value, start, end))); - size++; - return; - } - - int existingSize = store.get(name).size(); - List<Annotation> newList = new ArrayList<Annotation>(); - for (Annotation existing : store.get(name)) { - if (start > existing.getRange().getEnd() || end < existing.getRange().getStart()) { - // Add non-overlapping annotation to the new list as is. - newList.add(existing); - } else if (existing.getValue().equals(value)) { - // Merge the annotation. - start = Math.min(existing.getRange().getStart(), start); - end = Math.max(existing.getRange().getEnd(), end); - } else { - // Chop the bits off the existing annotation. - if (existing.getRange().getStart() < start) { - newList.add(new Annotation(existing.getName(), existing.getValue(), - existing.getRange().getStart(), start)); - } - if (existing.getRange().getEnd() > end) { - newList.add(new Annotation(existing.getName(), existing.getValue(), - end, existing.getRange().getEnd())); - } - } - } - newList.add(new Annotation(name, value, start, end)); - store.put(name, newList); - size += newList.size() - existingSize; - } - - /** - * Deletes an annotation. This is a no-op if the blip doesn't have this - * annotation. - * - * @param name the name of the annotation to be deleted. - * @param start the starting index of the annotation to be deleted. - * @param end the end index of the annotation to be deleted. - */ - void delete(String name, int start, int end) { - if (!store.containsKey(name)) { - return; - } - - int existingSize = store.get(name).size(); - List<Annotation> newList = new ArrayList<Annotation>(); - for (Annotation existing : store.get(name)) { - if (start > existing.getRange().getEnd() || end < existing.getRange().getStart()) { - newList.add(existing); - } else if (start < existing.getRange().getStart() && end > existing.getRange().getEnd()) { - continue; - } else { - // Chop the bits off the existing annotation. - if (existing.getRange().getStart() < start) { - newList.add(new Annotation(existing.getName(), existing.getValue(), - existing.getRange().getStart(), start)); - } - if (existing.getRange().getEnd() > end) { - newList.add(new Annotation(existing.getName(), existing.getValue(), - end, existing.getRange().getEnd())); - } - } - } - - if (!newList.isEmpty()) { - store.put(name, newList); - } else { - store.remove(name); - } - size -= existingSize - newList.size(); - } - - /** - * Shifts all annotations that have a range that is after or covers the given - * position. - * - * @param position the anchor position. - * @param shiftAmount the amount to shift the annotation range. - */ - void shift(int position, int shiftAmount) { - for (List<Annotation> annotations : store.values()) { - for (Annotation annotation : annotations) { - annotation.shift(position, shiftAmount); - } - } - - // Merge fragmented annotations that should be contiguous, for example: - // Annotation("foo", "bar", 1, 2) and Annotation("foo", "bar", 2, 3). - for (Entry<String, List<Annotation>> entry : store.entrySet()) { - List<Annotation> existingList = entry.getValue(); - List<Annotation> newList = new ArrayList<Annotation>(existingList.size()); - - for (int i = 0; i < existingList.size(); ++i) { - Annotation annotation = existingList.get(i); - String name = annotation.getName(); - String value = annotation.getValue(); - int start = annotation.getRange().getStart(); - int end = annotation.getRange().getEnd(); - - // Find the last end index. - for (int j = i + 1; j < existingList.size(); ++j) { - if (end < existingList.get(j).getRange().getStart()) { - break; - } - - if (end == existingList.get(j).getRange().getStart() && - value.equals(existingList.get(j).getValue())) { - end = existingList.get(j).getRange().getEnd(); - existingList.remove(j--); - } - } - newList.add(new Annotation(name, value, start, end)); - } - entry.setValue(newList); - } - } - - /** - * Returns a list of annotation instances that has the given name. - * - * @param name the annotation name. - * @return a list of {@link Annotation} instances in the owning blip that has - * the given name. - */ - public List<Annotation> get(String name) { - return store.get(name); - } - - /** - * Returns the number of distinct annotation names that the owning blip has. - * - * @return the number of distinct annotation names. - */ - public int size() { - return store.size(); - } - - /** - * Returns a set of annotation names that the owning blip has. - * - * @return a set of annotation names. - */ - public Set<String> namesSet() { - return new HashSet<String>(store.keySet()); - } - - /** - * Returns this {@link Annotations} object as a {@link List} of annotations. - * - * @return an unmodifiable list of annotations. - */ - public List<Annotation> asList() { - List<Annotation> annotations = new ArrayList<Annotation>(size); - for (Annotation annotation : this) { - annotations.add(annotation); - } - return Collections.unmodifiableList(annotations); - } - - @Override - public Iterator<Annotation> iterator() { - return new AnnotationIterator(store); - } - - /** - * An iterator over all annotations in this annotation set. Currently, it - * doesn't support {@code remove()} operation. - */ - private static class AnnotationIterator implements Iterator<Annotation> { - private Iterator<Annotation> listIterator; - private final Iterator<List<Annotation>> mapIterator; - - /** - * Constructor. - * - * @param store a map of annotation name to a list of annotations instances. - */ - private AnnotationIterator(Map<String, List<Annotation>> store) { - mapIterator = store.values().iterator(); - if (!store.isEmpty()) { - listIterator = mapIterator.next().iterator(); - } - } - - @Override - public boolean hasNext() { - return mapIterator.hasNext() || (listIterator != null && listIterator.hasNext()); - } - - @Override - public Annotation next() { - if (!listIterator.hasNext() && mapIterator.hasNext()) { - listIterator = mapIterator.next().iterator(); - } - return listIterator.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/com/google/wave/api/ApiIdSerializer.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/com/google/wave/api/ApiIdSerializer.java b/wave/src/main/java/com/google/wave/api/ApiIdSerializer.java deleted file mode 100644 index b950d69..0000000 --- a/wave/src/main/java/com/google/wave/api/ApiIdSerializer.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 com.google.wave.api; - -import org.waveprotocol.wave.model.id.DualIdSerialiser; -import org.waveprotocol.wave.model.id.IdSerialiser; - -/** - * Serializer for ids transmitted as part of the API. - * - * @author [email protected] (Alex North) - */ -public final class ApiIdSerializer { - - public static IdSerialiser instance() { - return DualIdSerialiser.MODERN; - } - - private ApiIdSerializer() { - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/com/google/wave/api/Attachment.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/com/google/wave/api/Attachment.java b/wave/src/main/java/com/google/wave/api/Attachment.java deleted file mode 100644 index 4fb9127..0000000 --- a/wave/src/main/java/com/google/wave/api/Attachment.java +++ /dev/null @@ -1,198 +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 com.google.wave.api; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Map; -import java.util.logging.Logger; - -/** - * Represents a wave attachment within a Wave. - */ -public class Attachment extends Element { - public static final String ATTACHMENT_ID = "attachmentId"; - public static final String CAPTION = "caption"; - public static final String MIME_TYPE = "mimeType"; - public static final String DATA = "data"; - public static final String ATTACHMENT_URL = "attachmentUrl"; - - private static final Logger LOG = Logger.getLogger(Attachment.class.getName()); - - /** Attachment data. */ - private byte[] data; - - /** - * Constructs an attachment with a given set of properties and data. - * - * @param properties the properties of the attachment. - * @param data the data of the attachment - */ - public Attachment(Map<String, String> properties, byte[] data) { - super(ElementType.ATTACHMENT, properties); - this.data = data; - } - - /** - * Constructs an attachment with data in bytes and caption. - * - * @param caption the caption of the attachment. - * @param data the attachment data as bytes. - */ - public Attachment(String caption, byte[] data) { - super(ElementType.ATTACHMENT); - setCaption(caption); - this.data = data; - } - - @Override - public void setProperty(String name, String value) { - if (name.equals(ATTACHMENT_ID) || name.equals(MIME_TYPE) || - name.equals(ATTACHMENT_URL) || name.equals(DATA)) { - throw new IllegalArgumentException(name + " can not be changed."); - } - super.setProperty(name, value); - } - - /** - * Returns the attachment id for the attachment. - * - * @return the attachment id for the attachment. - */ - public String getAttachmentId() { - return getProperty(ATTACHMENT_ID); - } - - /** - * Returns the url for the attachment. - * - * @return the url for the attachment. - */ - public String getAttachmentUrl() { - return getProperty(ATTACHMENT_URL); - } - - /** - * Sets the caption for the attachment. - * - * @param caption the caption to display for the attachment. - */ - public void setCaption(String caption) { - setProperty(CAPTION, caption); - } - - /** - * Returns the caption for the attachment. - * - * @return the caption for the attachment. - */ - public String getCaption() { - return getProperty(CAPTION); - } - - /** - * Returns the data for the attachment. Data will be fetched via HTTP if - * it's not available. - * - * @return the data for the attachment. - */ - public byte[] getData() { - if (data == null) { - try { - fetch(); - } catch (IOException e) { - LOG.info("Error fetching attachment data: " + e); - } - } - return data; - } - - public boolean hasData() { - return data != null; - } - - /** - * Returns the MIME type for the attachment. - * - * @return the MIME type for the attachment. - */ - public String getMimeType() { - return getProperty(MIME_TYPE); - } - - /** - * Creates an instance of {@link Restriction} that can be used to search for - * attachment with the given caption. - * - * @param caption the caption to filter. - * @return an instance of {@link Restriction}. - */ - public static Restriction restrictByCaption(String caption) { - return Restriction.of(CAPTION, caption); - } - - /** - * Creates an instance of {@link Restriction} that can be used to search for - * attachment with the given MIME type. - * - * @param mimeType the MIME type to filter. - * @return an instance of {@link Restriction}. - */ - public static Restriction restrictByMimeType(String mimeType) { - return Restriction.of(MIME_TYPE, mimeType); - } - - /** - * Creates an instance of {@link Restriction} that can be used to search for - * attachment with the given attachment id. - * - * @param attachmentId the id of the attachment. - * @return an instance of {@link Restriction}. - */ - public static Restriction restrictByAttachmentId(String attachmentId) { - return Restriction.of(ATTACHMENT_ID, attachmentId); - } - - private void fetch() throws IOException { - if (getAttachmentUrl() != null && (!getAttachmentUrl().isEmpty())) { - InputStream input = null; - ByteArrayOutputStream output = null; - try { - URL url = new URL(getAttachmentUrl()); - input = url.openStream(); - output = new ByteArrayOutputStream(); - int i; - while ((i = input.read()) != -1) { - output.write(i); - } - this.data = output.toByteArray(); - } finally { - if (input != null) { - input.close(); - } - if (output != null) { - output.close(); - } - } - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/com/google/wave/api/Blip.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/com/google/wave/api/Blip.java b/wave/src/main/java/com/google/wave/api/Blip.java deleted file mode 100644 index 111fb20..0000000 --- a/wave/src/main/java/com/google/wave/api/Blip.java +++ /dev/null @@ -1,804 +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 com.google.wave.api; - - -import org.waveprotocol.wave.model.id.WaveId; -import org.waveprotocol.wave.model.id.WaveletId; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A class that models a single blip instance. - * - * Blips are essentially the documents that make up a conversation, that contain - * annotations, content and elements. - */ -public class Blip implements Serializable { - - /** The property key for blip id in an inline blip element. */ - private static final String INLINE_BLIP_ELEMENT_ID_KEY = "id"; - - /** The {@link Pattern} object used to search markup content. */ - private static final Pattern MARKUP_PATTERN = Pattern.compile("\\<.*?\\>"); - - /** The id of this blip. */ - private final String blipId; - - /** The id of the parent blip, {@code null} for blips in the root thread. */ - private final String parentBlipId; - - /** The containing thread. */ - private final BlipThread thread; - - /** The ids of the children of this blip. */ - private final List<String> childBlipIds; - - /** The inline reply threads, sorted by location/offset. */ - private SortedMap<Integer, BlipThread> inlineReplyThreads; - - /** The reply threads. */ - private final List<BlipThread> replyThreads; - - /** The participant ids of the contributors of this blip. */ - private final List<String> contributors; - - /** The participant id of the creator of this blip. */ - private final String creator; - - /** The last modified time of this blip. */ - private final long lastModifiedTime; - - /** The version of this blip. */ - private final long version; - - /** The list of annotations for the content. */ - private final Annotations annotations; - - /** The wavelet that owns this blip. */ - @NonJsonSerializable private final Wavelet wavelet; - - /** The operation queue to queue operation to the robot proxy. */ - @NonJsonSerializable private final OperationQueue operationQueue; - - /** The blip content. */ - private String content; - - /** The element contents of this blip. */ - private SortedMap<Integer, Element> elements; - - /** - * Constructor. - * - * @param blipId the id of this blip. - * @param initialContent the initial content of the blip. - * @param parentBlipId the id of the parent. - * @param threadId the id of the containing thread. - * @param wavelet the wavelet that owns this blip. - */ - Blip(String blipId, String initialContent, String parentBlipId, String threadId, - Wavelet wavelet) { - this(blipId, new ArrayList<String>(), initialContent, new ArrayList<String>(), null, -1, -1, - parentBlipId, threadId, new ArrayList<Annotation>(), new TreeMap<Integer, Element>(), - new ArrayList<String>(), wavelet); - - // Make sure that initial content is valid, and starts with newline. - if (this.content == null || this.content.isEmpty()) { - this.content = "\n"; - } else if (!this.content.startsWith("\n")) { - this.content = "\n" + this.content; - } - } - - /** - * Constructor. - * - * @param blipId the id of this blip. - * @param childBlipIds he ids of the children of this blip. - * @param content the content of this blip. - * @param contributors the participant ids of the contributors of this blip. - * @param creator the participant id of the creator of this blip. - * @param lastModifiedTime the last modified time of this blip. - * @param version the version of this blip. - * @param parentBlipId the id of the parent of this blip. - * @param threadId the id of the parent thread of this blip. - * @param annotations the list of annotations for this blip's content. - * @param elements the element contents of this blip. - * @param replyThreadIds the ids of this blip's reply threads. - * @param wavelet the wavelet that owns this blip. - */ - Blip(String blipId, List<String> childBlipIds, String content, List<String> contributors, - String creator, long lastModifiedTime, long version, String parentBlipId, String threadId, - List<Annotation> annotations, Map<Integer, Element> elements, List<String> replyThreadIds, - Wavelet wavelet) { - this.blipId = blipId; - this.content = content; - this.childBlipIds = new ArrayList<String>(childBlipIds); - this.contributors = new ArrayList<String>(contributors); - this.creator = creator; - this.lastModifiedTime = lastModifiedTime; - this.version = version; - this.parentBlipId = parentBlipId; - this.thread = wavelet.getThread(threadId); - - this.annotations = new Annotations(); - for (Annotation annotation : annotations) { - this.annotations.add(annotation.getName(), annotation.getValue(), - annotation.getRange().getStart(), annotation.getRange().getEnd()); - } - - this.elements = new TreeMap<Integer, Element>(elements); - this.wavelet = wavelet; - this.operationQueue = wavelet.getOperationQueue(); - - // Populate reply threads. - this.inlineReplyThreads = new TreeMap<Integer, BlipThread>(); - this.replyThreads = new ArrayList<BlipThread>(); - for (String replyThreadId : replyThreadIds) { - BlipThread thread = wavelet.getThread(replyThreadId); - if (thread.getLocation() != -1) { - inlineReplyThreads.put(thread.getLocation(), thread); - } else { - replyThreads.add(thread); - } - } - } - - /** - * Shallow copy constructor. - * - * @param other the blip to copy. - * @param operationQueue the operation queue for this new blip instance. - */ - private Blip(Blip other, OperationQueue operationQueue) { - this.blipId = other.blipId; - this.childBlipIds = other.childBlipIds; - this.inlineReplyThreads = other.inlineReplyThreads; - this.replyThreads = other.replyThreads; - this.content = other.content; - this.contributors = other.contributors; - this.creator = other.creator; - this.lastModifiedTime = other.lastModifiedTime; - this.version = other.version; - this.parentBlipId = other.parentBlipId; - this.thread = other.thread; - this.annotations = other.annotations; - this.elements = other.elements; - this.wavelet = other.wavelet; - this.operationQueue = operationQueue; - } - - /** - * Returns the id of this blip. - * - * @return the blip id. - */ - public String getBlipId() { - return blipId; - } - - /** - * Returns the id of the wave that owns this blip. - * - * @return the wave id. - */ - public WaveId getWaveId() { - return wavelet.getWaveId(); - } - - /** - * Returns the id of the wavelet that owns this blip. - * - * @return the wavelet id. - */ - public WaveletId getWaveletId() { - return wavelet.getWaveletId(); - } - - /** - * Returns the list of ids of this blip children. - * - * @return the children's ids. - */ - public List<String> getChildBlipIds() { - return childBlipIds; - } - - /** - * Returns the list of child blips. - * - * @return the children of this blip. - */ - public List<Blip> getChildBlips() { - List<Blip> result = new ArrayList<Blip>(childBlipIds.size()); - for (String childId : childBlipIds) { - Blip childBlip = wavelet.getBlips().get(childId); - if (childBlip != null) { - result.add(childBlip); - } - } - return result; - } - - /** - * @return the inline reply threads of this blip, sorted by the offset. - */ - public Collection<BlipThread> getInlineReplyThreads() { - return inlineReplyThreads.values(); - } - - /** - * @return the reply threads of this blip. - */ - public Collection<BlipThread> getReplyThreads() { - return replyThreads; - } - - /** - * Returns the participant ids of the contributors of this blip. - * - * @return the blip's contributors. - */ - public List<String> getContributors() { - return contributors; - } - - /** - * Returns the participant id of the creator of this blip. - * - * @return the blip's creator. - */ - public String getCreator() { - return creator; - } - - /** - * Returns the last modified time of this blip. - * - * @return the blip's last modified time. - */ - public long getLastModifiedTime() { - return lastModifiedTime; - } - - /** - * Returns the version of this blip. - * - * @return the blip's version. - */ - public long getVersion() { - return version; - } - - /** - * Returns the id of this blip's parent, or {@code null} if this blip is in - * the root thread. - * - * @return the blip's parent's id. - */ - public String getParentBlipId() { - return parentBlipId; - } - - /** - * Returns the parent blip. - * - * @return the parent of this blip. - */ - public Blip getParentBlip() { - if (parentBlipId == null) { - return null; - } - return wavelet.getBlips().get(parentBlipId); - } - - /** - * @return the containing thread. - */ - public BlipThread getThread() { - return thread; - } - - /** - * Checks whether this is a root blip or not. - * - * @return {@code true} if this is a root blip, denoted by {@code null} parent - * id. - */ - public boolean isRoot() { - return blipId.equals(wavelet.getRootBlipId()); - } - - /** - * Returns the annotations for this blip's content. - * - * @return the blip's annotations. - */ - public Annotations getAnnotations() { - return annotations; - } - - /** - * Returns the elements content of this blip. - * - * @return the blip's elements. - */ - public SortedMap<Integer, Element> getElements() { - return elements; - } - - /** - * Returns the text content of this blip. - * - * @return blip's content. - */ - public String getContent() { - return content; - } - - /** - * Sets the content of this blip. - * - * @param content the blip's content. - */ - void setContent(String content) { - if (!content.startsWith("\n")) { - content = "\n" + content; - } - this.content = content; - } - - /** - * Returns the length/size of the blip, denoted by the length of this blip's - * text content. - * - * @return the size of the blip. - */ - public int length() { - return content.length(); - } - - /** - * Returns the wavelet that owns this Blip. - * - * @return the wavelet. - */ - public Wavelet getWavelet() { - return wavelet; - } - - /** - * Returns the operation queue for sending outgoing operations to the robot - * proxy. - * - * @return the operation queue. - */ - protected OperationQueue getOperationQueue() { - return operationQueue; - } - - /** - * Returns a reference to the entire content of the blip. - * - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs all() { - return BlipContentRefs.all(this); - } - - /** - * Returns all references to this blip's content that match {@code target}. - * - * @param target the text to search for. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs all(String target) { - return BlipContentRefs.all(this, target, -1); - } - - /** - * Returns all references to this blip's content that match {@code target}. - * This blip references object will have at most {@code maxResult} hits. - * - * @param target the text to search for. - * @param maxResult the maximum number of hits. Specify -1 for no limit. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs all(String target, int maxResult) { - return BlipContentRefs.all(this, target, maxResult); - } - - /** - * Returns all references to this blip's content that match {@code target} and - * {@code restrictions}. - * - * @param target the element type to search for. - * @param restrictions the element properties that need to be matched. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs all(ElementType target, Restriction... restrictions) { - return BlipContentRefs.all(this, target, -1, restrictions); - } - - /** - * Returns all references to this blip's content that match {@code target} and - * {@code restrictions}. This blip references object will have at most - * {@code maxResult} hits. - * - * @param target the element type to search for. - * @param maxResult the maximum number of hits. Specify -1 for no limit. - * @param restrictions the element properties that need to be matched. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs all(ElementType target, int maxResult, Restriction... restrictions) { - return BlipContentRefs.all(this, target, maxResult, restrictions); - } - - /** - * Returns the first reference to this blip's content that matches - * {@code target}. - * - * @param target the text to search for. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs first(String target) { - return all(target, 1); - } - - /** - * Returns the first reference to this blip's content that matches - * {@code target} and {@code restrictions}. - * - * @param target the type of element to search for. - * @param restrictions the list of restrictions to filter the search. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs first(ElementType target, Restriction... restrictions) { - return all(target, 1, restrictions); - } - - /** - * Returns the reference to this blip's content at the specified index. - * - * @param index the index to reference. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs at(int index) { - return BlipContentRefs.range(this, index, index + 1); - } - - /** - * Returns the reference to this blip's content at the specified range. - * - * @param start the start index of the range to reference. - * @param end the end index of the range to reference. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs range(int start, int end) { - return BlipContentRefs.range(this, start, end); - } - - /** - * Appends the given argument (element, text, or markup) to the blip. - * - * @param argument the element, text, or markup to be appended. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs append(BlipContent argument) { - return BlipContentRefs.all(this).insertAfter(argument); - } - - /** - * Appends the given string to the blip. - * - * @param argument the string to be appended. - * @return an instance of {@link BlipContentRefs}. - */ - public BlipContentRefs append(String argument) { - return BlipContentRefs.all(this).insertAfter(argument); - } - - /** - * Creates a reply to this blip. - * - * @return an instance of {@link Blip} that represents a reply to the blip. - */ - public Blip reply() { - return operationQueue.createChildOfBlip(this); - } - - /** - * Continues the containing thread of this blip.. - * - * @return an instance of {@link Blip} that represents a the new continuation - * reply blip. - */ - public Blip continueThread() { - return operationQueue.continueThreadOfBlip(this); - } - - /** - * Inserts an inline blip at the given position. - * - * @param position the index to insert the inline blip at. This has to be - * greater than 0. - * @return an instance of {@link Blip} that represents the new inline blip. - */ - public Blip insertInlineBlip(int position) { - if (position <= 0 || position > content.length()) { - throw new IllegalArgumentException("Illegal inline blip position: " + position + - ". Position has to be greater than 0 and less than or equal to length."); - } - - // Shift the elements. - shift(position, 1); - content = content.substring(0, position) + " " + content.substring(position); - - // Generate the operation. - Blip inlineBlip = operationQueue.insertInlineBlipToDocument(this, position); - - // Insert the inline blip element. - Element element = new Element(ElementType.INLINE_BLIP); - element.setProperty(INLINE_BLIP_ELEMENT_ID_KEY, inlineBlip.getBlipId()); - elements.put(position, element); - return inlineBlip; - } - - /** - * Appends markup ({@code HTML}) content. - * - * @param markup the markup content to add. - */ - public void appendMarkup(String markup) { - operationQueue.appendMarkupToDocument(this, markup); - this.content += convertToPlainText(markup); - } - - /** - * Returns a view of this blip that will proxy for the specified id. - * - * A shallow copy of the current blip is returned with the {@code proxyingFor} - * field set. Any modifications made to this copy will be done using the - * {@code proxyForId}, i.e. the {@code robot+<proxyForId>@appspot.com} address - * will be used. - * - * @param proxyForId the id to proxy. Please note that this parameter should - * be properly encoded to ensure that the resulting participant id is - * valid (see {@link Util#checkIsValidProxyForId(String)} for more - * details). - * @return a shallow copy of this blip with the proxying information set. - */ - public Blip proxyFor(String proxyForId) { - Util.checkIsValidProxyForId(proxyForId); - OperationQueue proxiedOperationQueue = operationQueue.proxyFor(proxyForId); - return new Blip(this, proxiedOperationQueue); - } - - /** - * Returns the offset of this blip if it is inline, or -1 if it's not. If the - * parent is not in the offset, this method will always return -1 since it - * can't determine the inline blip status. - * - * @return the offset of this blip if it is inline, or -1 if it's not inline - * or if the parent is not in the context. - * @deprecated please use {@code getThread().getLocation()} to get the offset - * of the inline reply thread that contains this blip. - */ - @Deprecated - public int getInlineBlipOffset() { - Blip parent = getParentBlip(); - if (parent == null) { - return -1; - } - - for (Entry<Integer, Element> entry : parent.getElements().entrySet()) { - Element element = entry.getValue(); - if (element.getType() == ElementType.INLINE_BLIP && - blipId.equals(element.getProperty(INLINE_BLIP_ELEMENT_ID_KEY))) { - return entry.getKey(); - } - } - return -1; - } - - /** - * Moves all elements and annotations after the given position by - * {@code shiftAmount}. - * - * @param position the anchor position. - * @param shiftAmount the amount to shift the annotations range and elements - * position. - */ - protected void shift(int position, int shiftAmount) { - SortedMap<Integer, Element> newElements = - new TreeMap<Integer, Element>(elements.headMap(position)); - for (Entry<Integer, Element> element : elements.tailMap(position).entrySet()) { - newElements.put(element.getKey() + shiftAmount, element.getValue()); - } - this.elements = newElements; - - SortedMap<Integer, BlipThread> newInlineReplyThreads = - new TreeMap<Integer, BlipThread>(inlineReplyThreads.headMap(position)); - for (Entry<Integer, BlipThread> entry : inlineReplyThreads.tailMap(position).entrySet()) { - BlipThread thread = entry.getValue(); - thread.setLocation(thread.getLocation() + shiftAmount); - newInlineReplyThreads.put(thread.getLocation(), thread); - } - this.inlineReplyThreads = newInlineReplyThreads; - - this.annotations.shift(position, shiftAmount); - } - - /** - * Deletes all annotations that span from {@code start} to {@code end}. - * - * @param start the start position. - * @param end the end position. - */ - protected void deleteAnnotations(int start, int end) { - for (String name : annotations.namesSet()) { - annotations.delete(name, start, end); - } - } - - /** - * Deletes the given blip id from the list of child blip ids. - * - * @param childBlipId the blip id to delete. - */ - protected void deleteChildBlipId(String childBlipId) { - this.childBlipIds.remove(childBlipId); - } - - /** - * Adds the given {@link BlipThread} as a reply or inline reply thread. - * - * @param thread the new thread to add. - */ - protected void addThread(BlipThread thread) { - if (thread.getLocation() == -1) { - this.replyThreads.add(thread); - } else { - this.inlineReplyThreads.put(thread.getLocation(), thread); - } - } - - /** - * Removes the given {@link BlipThread} from the reply or inline reply thread. - * - * @param thread the new thread to remove. - */ - protected void removeThread(BlipThread thread) { - if (thread.getLocation() == -1) { - this.replyThreads.remove(thread); - } else { - this.inlineReplyThreads.remove(thread.getLocation()); - } - } - - /** - * Converts the given {@code HTML} into robot compatible plaintext. - * - * @param html the {@code HTML} to convert. - * @return a plain text version of the given {@code HTML}. - */ - private static String convertToPlainText(String html) { - StringBuffer result = new StringBuffer(); - Matcher matcher = MARKUP_PATTERN.matcher(html); - while (matcher.find()) { - String replacement = ""; - String tag = matcher.group().substring(1, matcher.group().length() - 1).split(" ")[0]; - if ("p".equals(tag) || "br".equals(tag)) { - replacement = "\n"; - } - matcher.appendReplacement(result, replacement); - } - matcher.appendTail(result); - return result.toString(); - } - - /** - * Serializes this {@link Blip} into a {@link BlipData}. - * - * @return an instance of {@link BlipData} that represents this blip. - */ - public BlipData serialize() { - BlipData blipData = new BlipData(); - - // Add primitive properties. - blipData.setBlipId(blipId); - blipData.setWaveId(ApiIdSerializer.instance().serialiseWaveId(wavelet.getWaveId())); - blipData.setWaveletId(ApiIdSerializer.instance().serialiseWaveletId(wavelet.getWaveletId())); - blipData.setParentBlipId(parentBlipId); - blipData.setThreadId(thread.getId()); - blipData.setCreator(creator); - blipData.setLastModifiedTime(lastModifiedTime); - blipData.setVersion(version); - blipData.setContent(content); - - // Add list and map properties. - blipData.setChildBlipIds(childBlipIds); - blipData.setContributors(contributors); - blipData.setElements(elements); - - // Add annotations. - List<Annotation> annotations = new ArrayList<Annotation>(); - for (Annotation annotation : this.annotations) { - annotations.add(annotation); - } - blipData.setAnnotations(annotations); - - // Add reply threads ids. - List<String> replyThreadIds = new ArrayList<String>(inlineReplyThreads.size() + - replyThreads.size()); - for (BlipThread thread : inlineReplyThreads.values()) { - replyThreadIds.add(thread.getId()); - } - for (BlipThread thread : replyThreads) { - replyThreadIds.add(thread.getId()); - } - blipData.setReplyThreadIds(replyThreadIds); - - return blipData; - } - - /** - * Deserializes the given {@link BlipData} object into an instance of - * {@link Blip}. - * - * @param operationQueue the operation queue. - * @param wavelet the wavelet that owns this blip. - * @param blipData the blip data to be deserialized. - * @return an instance of {@link Wavelet}. - */ - public static Blip deserialize(OperationQueue operationQueue, Wavelet wavelet, - BlipData blipData) { - // Extract primitive properties. - String blipId = blipData.getBlipId(); - String parentBlipId = blipData.getParentBlipId(); - String threadId = blipData.getThreadId(); - String creator = blipData.getCreator(); - long lastModifiedTime = blipData.getLastModifiedTime(); - long version = blipData.getVersion(); - String content = blipData.getContent(); - - List<String> childBlipIds = blipData.getChildBlipIds(); - List<String> replyThreadIds = blipData.getReplyThreadIds(); - if (replyThreadIds == null) { - replyThreadIds = new ArrayList<String>(); - } - - List<String> contributors = blipData.getContributors(); - Map<Integer, Element> elements = blipData.getElements(); - - List<Annotation> annotations = blipData.getAnnotations(); - return new Blip(blipId, childBlipIds, content, contributors, creator, lastModifiedTime, - version, parentBlipId, threadId, annotations, elements, replyThreadIds, wavelet); - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/051db092/wave/src/main/java/com/google/wave/api/BlipContent.java ---------------------------------------------------------------------- diff --git a/wave/src/main/java/com/google/wave/api/BlipContent.java b/wave/src/main/java/com/google/wave/api/BlipContent.java deleted file mode 100644 index 25ce244..0000000 --- a/wave/src/main/java/com/google/wave/api/BlipContent.java +++ /dev/null @@ -1,65 +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 com.google.wave.api; - -import java.io.Serializable; - -/** - * An abstract class that models a blip content. - * - * @see Element - * @see Plaintext - */ -public abstract class BlipContent implements Serializable { - - /** - * Returns the textual representation of of this blip content, for example, - * if it's an element, it returns a space. - * - * @return the textual representation. - */ - public abstract String getText(); - - /** - * Returns this blip content as an element. - * - * @return an instance of {@link Element}, or {@code null} if it is not an - * {@link Element}. - */ - public Element asElement() { - if (!(this instanceof Element)) { - return null; - } - return Element.class.cast(this); - } - - /** - * Returns this blip content as a plain-text. - * - * @return an instance of {@link Plaintext}, or {@code null} if it is not a - * {@link Plaintext}. - */ - public Plaintext asPlaintext() { - if (!(this instanceof Plaintext)) { - return null; - } - return Plaintext.class.cast(this); - } -}
