All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.rpc.client.impl.SimplePayloadSink Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * Copyright 2009 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.rpc.client.impl;

import com.google.gwt.core.client.GWT;
import com.google.gwt.rpc.client.ast.ArrayValueCommand;
import com.google.gwt.rpc.client.ast.BooleanValueCommand;
import com.google.gwt.rpc.client.ast.ByteValueCommand;
import com.google.gwt.rpc.client.ast.CharValueCommand;
import com.google.gwt.rpc.client.ast.CommandSink;
import com.google.gwt.rpc.client.ast.DoubleValueCommand;
import com.google.gwt.rpc.client.ast.EnumValueCommand;
import com.google.gwt.rpc.client.ast.FloatValueCommand;
import com.google.gwt.rpc.client.ast.InstantiateCommand;
import com.google.gwt.rpc.client.ast.IntValueCommand;
import com.google.gwt.rpc.client.ast.InvokeCustomFieldSerializerCommand;
import com.google.gwt.rpc.client.ast.LongValueCommand;
import com.google.gwt.rpc.client.ast.NullValueCommand;
import com.google.gwt.rpc.client.ast.ReturnCommand;
import com.google.gwt.rpc.client.ast.RpcCommand;
import com.google.gwt.rpc.client.ast.RpcCommandVisitor;
import com.google.gwt.rpc.client.ast.SetCommand;
import com.google.gwt.rpc.client.ast.ShortValueCommand;
import com.google.gwt.rpc.client.ast.StringValueCommand;
import com.google.gwt.rpc.client.ast.ThrowCommand;
import com.google.gwt.rpc.client.ast.ValueCommand;
import com.google.gwt.user.client.rpc.SerializationException;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * This implementation of CommandSink encodes RpcCommands in a simple transport
 * format that can be interpreted by both the client and the server.
 */
public class SimplePayloadSink extends CommandSink {

  private class Visitor extends RpcCommandVisitor {

    @Override
    public void endVisit(BooleanValueCommand x, Context ctx) {
      appendTypedData(BOOLEAN_TYPE, x.getValue() ? "1" : "0");
    }

    @Override
    public void endVisit(ByteValueCommand x, Context ctx) {
      appendTypedData(BYTE_TYPE, x.getValue().toString());
    }

    @Override
    public void endVisit(CharValueCommand x, Context ctx) {
      appendTypedData(CHAR_TYPE, String.valueOf((int) x.getValue()));
    }

    @Override
    public void endVisit(DoubleValueCommand x, Context ctx) {
      appendTypedData(DOUBLE_TYPE, x.getValue().toString());
    }

    @Override
    public void endVisit(EnumValueCommand x, Context ctx) {
      // ETypeSeedName~IOrdinal~
      if (appendIdentity(x)) {
        appendTypedData(ENUM_TYPE, x.getValue().getDeclaringClass().getName());
        // use ordinal (and not name), since name might have been obfuscated
        appendTypedData(INT_TYPE, String.valueOf(x.getValue().ordinal()));
      }
    }

    @Override
    public void endVisit(FloatValueCommand x, Context ctx) {
      appendTypedData(FLOAT_TYPE, x.getValue().toString());
    }

    @Override
    public void endVisit(IntValueCommand x, Context ctx) {
      appendTypedData(INT_TYPE, x.getValue().toString());
    }

    @Override
    public void endVisit(LongValueCommand x, Context ctx) {
      appendTypedData(LONG_TYPE, x.getValue().toString());
    }

    @Override
    public void endVisit(NullValueCommand x, Context ctx) {
      appendTypedData(VOID_TYPE, "");
    }

    @Override
    public void endVisit(ShortValueCommand x, Context ctx) {
      appendTypedData(SHORT_TYPE, x.getValue().toString());
    }

    @Override
    public void endVisit(StringValueCommand x, Context ctx) {
      // "4~abcd
      if (appendIdentity(x)) {
        String value = x.getValue();
        /*
         * Emit this a a Pascal-style string, using an explicit length. This
         * avoids the need to escape the value.
         */
        appendTypedData(STRING_TYPE, String.valueOf(value.length()));
        append(value);
      }
    }

    @Override
    public boolean visit(ArrayValueCommand x, Context ctx) {
      /*
       * Encoded as (leafType, dimensions, length, .... )
       * 
       * Object[] foo = new Object[3];
       * 
       * becomes
       * 
       * [ObjectSeedname~1~3~@....~@....~@...~
       * 
       * Object[][] foo = new Object[3][];
       * 
       * becomes
       * 
       * [ObjectSeedName~2~3~...three one-dim arrays...
       */
      if (appendIdentity(x)) {
        int dims = 1;
        Class leaf = x.getComponentType();
        while (leaf.getComponentType() != null) {
          dims++;
          leaf = leaf.getComponentType();
        }

        appendTypedData(ARRAY_TYPE, leaf.getName());
        accept(new IntValueCommand(dims));
        accept(new IntValueCommand(x.getComponentValues().size()));
        return true;
      } else {
        return false;
      }
    }

    @Override
    public boolean visit(InstantiateCommand x, Context ctx) {
      // @TypeSeedName~3~... N-many setters ...
      if (appendIdentity(x)) {
        appendTypedData(OBJECT_TYPE, x.getTargetClass().getName());
        accept(new IntValueCommand(x.getSetters().size()));
        return true;
      } else {
        return false;
      }
    }

    @Override
    public boolean visit(InvokeCustomFieldSerializerCommand x, Context ctx) {
      // !TypeSeedName~Number of objects written by CFS~...CFS objects...~
      // Number of extra fields~...N-many setters...
      if (appendIdentity(x)) {
        appendTypedData(INVOKE_TYPE, x.getTargetClass().getName());
        accept(new IntValueCommand(x.getValues().size()));
        accept(x.getValues());
        accept(new IntValueCommand(x.getSetters().size()));
        accept(x.getSetters());
        return false;
      } else {
        return false;
      }
    }

    @Override
    public boolean visit(ReturnCommand x, Context ctx) {
      // R4~...values...
      appendTypedData(RETURN_TYPE, String.valueOf(x.getValues().size()));
      return true;
    }

    @Override
    public boolean visit(SetCommand x, Context ctx) {
      /*
       * In Development Mode, the field's declaring class is written to the
       * stream to handle field shadowing. In Production Mode, this isn't
       * necessary because all field names are allocated in the same "object"
       * scope.
       *
       * DeclaringClassName~FieldName~...value...
       */
      if (!GWT.isScript()) {
        accept(new StringValueCommand(x.getFieldDeclClass().getName()));
      }
      accept(new StringValueCommand(x.getField()));
      return true;
    }

    @Override
    public boolean visit(ThrowCommand x, Context ctx) {
      // T...value...
      appendTypedData(THROW_TYPE, "");
      return true;
    }

    private void append(String x) {
      try {
        buffer.append(EscapeUtil.escape(x)).append(RPC_SEPARATOR_CHAR);
      } catch (IOException e) {
        halt(e);
      }
    }

    private boolean appendIdentity(ValueCommand x) {
      Integer backRef = backRefs.get(x);
      if (backRef != null) {
        if (PRETTY) {
          try {
            buffer.append(NL_CHAR);
          } catch (IOException e) {
            halt(e);
          }
        }
        append(BACKREF_TYPE + String.valueOf(backRef));
        return false;
      } else {
        backRefs.put(x, backRefs.size());
        return true;
      }
    }

    private void appendTypedData(char type, String value) {
      try {
        if (PRETTY) {
          buffer.append(NL_CHAR);
        }
        buffer.append(type).append(value).append(RPC_SEPARATOR_CHAR);
      } catch (IOException e) {
        halt(e);
      }
    }
  }

  /**
   * Used for diagnostics.
   */
  static final boolean PRETTY = false;

  public static final char ARRAY_TYPE = '[';
  public static final char BACKREF_TYPE = '@';
  public static final char BOOLEAN_TYPE = 'Z';
  public static final char BYTE_TYPE = 'B';
  public static final char CHAR_TYPE = 'C';
  public static final char DOUBLE_TYPE = 'D';
  public static final char ENUM_TYPE = 'E';
  public static final char FLOAT_TYPE = 'F';
  public static final char INT_TYPE = 'I';
  public static final char INVOKE_TYPE = '!';
  public static final char LONG_TYPE = 'J';
  public static final char NL_CHAR = '\n';
  public static final char OBJECT_TYPE = 'L';
  public static final char RETURN_TYPE = 'R';
  public static final char RPC_SEPARATOR_CHAR = '~';
  public static final char SHORT_TYPE = 'S';
  public static final char STRING_TYPE = '"';
  public static final char THROW_TYPE = 'T';
  public static final char VOID_TYPE = 'V';

  private final Map backRefs = new HashMap();

  private final Appendable buffer;

  public SimplePayloadSink(Appendable buffer) {
    this.buffer = buffer;
  }

  @Override
  public void accept(RpcCommand command) throws SerializationException {
    (new Visitor()).accept(command);
  }

  @Override
  public void finish() throws SerializationException {
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy