Skip to content

Commit

Permalink
Basic support for JavaScriptObject
Browse files Browse the repository at this point in the history
Only support the basic stringify and JsonUtils.safeEval.
Later, it will be possible to add any jackson annotation on them. The generator will detect it and will consider the JSO as any other bean. The only difference should be the default constructor.
There is also some edge case  around JsArray. What if the JSO contains in JsArray has jackson annotations ?
  • Loading branch information
nmorel committed Jan 18, 2015
1 parent ba32fda commit 2484905
Show file tree
Hide file tree
Showing 17 changed files with 669 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import com.github.nmorel.gwtjackson.client.exception.JsonDeserializationException;
import com.github.nmorel.gwtjackson.client.stream.JsonReader;
import com.github.nmorel.gwtjackson.client.stream.impl.NonBufferedJsonReader;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsonUtils;

/**
* Context for the deserialization process.
Expand All @@ -43,6 +45,8 @@ public static class Builder {

private boolean wrapExceptions = true;

private boolean useSafeEval = true;

/**
* Determines whether encountering of unknown
* properties (ones that do not map to a property, and there is
Expand Down Expand Up @@ -115,8 +119,21 @@ public Builder wrapExceptions( boolean wrapExceptions ) {
return this;
}

/**
* Feature that determines whether gwt-jackson should use {@link JsonUtils#safeEval(String)} or {@link JsonUtils#unsafeEval(String)}
* to deserialize {@link JavaScriptObject}
* <br>
* <br>
* {@link JsonUtils#safeEval(String)} is used by default.
*/
public Builder useSafeEval( boolean useSafeEval ) {
this.useSafeEval = useSafeEval;
return this;
}

public JsonDeserializationContext build() {
return new JsonDeserializationContext( failOnUnknownProperties, unwrapRootValue, acceptSingleValueAsArray, wrapExceptions );
return new JsonDeserializationContext( failOnUnknownProperties, unwrapRootValue, acceptSingleValueAsArray, wrapExceptions,
useSafeEval );
}
}

Expand All @@ -135,12 +152,15 @@ public JsonDeserializationContext build() {

private final boolean wrapExceptions;

private final boolean useSafeEval;

private JsonDeserializationContext( boolean failOnUnknownProperties, boolean unwrapRootValue, boolean acceptSingleValueAsArray,
boolean wrapExceptions ) {
boolean wrapExceptions, boolean useSafeEval ) {
this.failOnUnknownProperties = failOnUnknownProperties;
this.unwrapRootValue = unwrapRootValue;
this.acceptSingleValueAsArray = acceptSingleValueAsArray;
this.wrapExceptions = wrapExceptions;
this.useSafeEval = useSafeEval;
}

@Override
Expand Down Expand Up @@ -169,6 +189,13 @@ public boolean isAcceptSingleValueAsArray() {
return acceptSingleValueAsArray;
}

/**
* @see Builder#useSafeEval(boolean)
*/
public boolean isUseSafeEval() {
return useSafeEval;
}

public JsonReader newJsonReader( String input ) {
JsonReader reader = new NonBufferedJsonReader( input );
reader.setLenient( true );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2015 Nicolas Morel
*
* 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.github.nmorel.gwtjackson.client.deser;

import com.github.nmorel.gwtjackson.client.JsonDeserializationContext;
import com.github.nmorel.gwtjackson.client.JsonDeserializer;
import com.github.nmorel.gwtjackson.client.JsonDeserializerParameters;
import com.github.nmorel.gwtjackson.client.stream.JsonReader;
import com.google.gwt.core.client.JavaScriptObject;

/**
* Default {@link JsonDeserializer} implementation for {@link JavaScriptObject}.
*
* @author Nicolas Morel
*/
public class JavaScriptObjectJsonDeserializer<T extends JavaScriptObject> extends JsonDeserializer<T> {

private static final JavaScriptObjectJsonDeserializer INSTANCE = new JavaScriptObjectJsonDeserializer();

/**
* @return an instance of {@link JavaScriptObjectJsonDeserializer}
*/
public static JavaScriptObjectJsonDeserializer getInstance() {
return INSTANCE;
}

private JavaScriptObjectJsonDeserializer() { }

@Override
public T doDeserialize( JsonReader reader, JsonDeserializationContext ctx, JsonDeserializerParameters params ) {
return reader.nextJavaScriptObject( ctx.isUseSafeEval() ).cast();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2015 Nicolas Morel
*
* 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.github.nmorel.gwtjackson.client.ser;

import javax.annotation.Nonnull;

import com.github.nmorel.gwtjackson.client.JsonSerializationContext;
import com.github.nmorel.gwtjackson.client.JsonSerializer;
import com.github.nmorel.gwtjackson.client.JsonSerializerParameters;
import com.github.nmorel.gwtjackson.client.stream.JsonWriter;
import com.google.gwt.core.client.JavaScriptObject;

/**
* Default {@link JsonSerializer} implementation for {@link JavaScriptObject}.
*
* @author Nicolas Morel
*/
public class JavaScriptObjectJsonSerializer<T extends JavaScriptObject> extends JsonSerializer<T> {

private static final JavaScriptObjectJsonSerializer INSTANCE = new JavaScriptObjectJsonSerializer();

/**
* @return an instance of {@link JavaScriptObjectJsonSerializer}
*/
public static JavaScriptObjectJsonSerializer getInstance() {
return INSTANCE;
}

private JavaScriptObjectJsonSerializer() { }

@Override
public void doSerialize( JsonWriter writer, @Nonnull T value, JsonSerializationContext ctx, JsonSerializerParameters params ) {
writer.value( value );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

import java.math.BigInteger;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsonUtils;

public interface JsonReader
{

Expand Down Expand Up @@ -191,4 +194,13 @@ public interface JsonReader
* @throws NumberFormatException if the next value cannot be parsed as a number.
*/
Number nextNumber();

/**
* Returns the {@link JavaScriptObject} of the next token, consuming it.
*
* @param useSafeEval whether it should use {@link JsonUtils#safeEval(String)} or {@link JsonUtils#unsafeEval(String)}
*
* @return the {@link JavaScriptObject}
*/
JavaScriptObject nextJavaScriptObject( boolean useSafeEval );
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.github.nmorel.gwtjackson.client.stream;

import com.google.gwt.core.client.JavaScriptObject;

public interface JsonWriter {

/**
Expand Down Expand Up @@ -153,6 +155,14 @@ public interface JsonWriter {
*/
JsonWriter value( Number value );

/**
* Encodes {@code value}.
*
* @param value a value .
* @return this writer.
*/
JsonWriter value( JavaScriptObject value );

/**
* Encodes {@code value}.toString() as is.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@

import com.github.nmorel.gwtjackson.client.exception.JsonDeserializationException;
import com.github.nmorel.gwtjackson.client.stream.JsonToken;
import com.github.nmorel.gwtjackson.client.stream.JsonWriter;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayInteger;
import com.google.gwt.core.client.JsonUtils;

/**
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
Expand Down Expand Up @@ -1484,7 +1487,9 @@ public String nextValue()
return "null";
}

StringBuilder builder = new StringBuilder();
JsonWriter writer = new DefaultJsonWriter( new StringBuilder() );
writer.setLenient( true );

int count = 0;
do {
p = peeked;
Expand All @@ -1495,58 +1500,48 @@ public String nextValue()
if (p == PEEKED_BEGIN_ARRAY) {
push(JsonScope.EMPTY_ARRAY);
count++;
builder.append('[');
writer.beginArray();
} else if (p == PEEKED_BEGIN_OBJECT) {
push(JsonScope.EMPTY_OBJECT);
count++;
builder.append('{');
writer.beginObject();
} else if (p == PEEKED_END_ARRAY) {
stackSize--;
count--;
builder.append(']');
writer.endArray();
} else if (p == PEEKED_END_OBJECT) {
stackSize--;
count--;
builder.append('}');
writer.endObject();
} else if (p == PEEKED_UNQUOTED_NAME) {
builder.append(nextUnquotedValue());
builder.append(':');
writer.name( nextUnquotedValue() );
} else if (p == PEEKED_SINGLE_QUOTED_NAME) {
builder.append('\'');
builder.append(nextQuotedValue( '\'' ));
builder.append('\'');
builder.append(':');
writer.name( nextQuotedValue( '\'' ) );
} else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
builder.append('"');
builder.append(nextQuotedValue( '"' ));
builder.append('"');
builder.append(':');
writer.name( nextQuotedValue( '"' ) );
} else if (p == PEEKED_UNQUOTED) {
builder.append(nextUnquotedValue());
writer.value( nextUnquotedValue() );
} else if (p == PEEKED_SINGLE_QUOTED) {
builder.append('\'');
builder.append(nextQuotedValue( '\'' ));
builder.append('\'');
writer.value(nextQuotedValue( '\'' ));
} else if (p == PEEKED_DOUBLE_QUOTED) {
builder.append('"');
builder.append(nextQuotedValue( '"' ));
builder.append('"');
writer.value(nextQuotedValue( '"' ));
} else if (p == PEEKED_NUMBER) {
builder.append( new String(buffer, pos, peekedNumberLength) );
writer.value( new String(buffer, pos, peekedNumberLength) );
pos += peekedNumberLength;
} else if (p == PEEKED_TRUE) {
builder.append( true );
writer.value(true);
} else if (p == PEEKED_FALSE) {
builder.append( false );
writer.value( false );
} else if (p == PEEKED_LONG) {
builder.append( peekedLong );
writer.value( peekedLong );
} else if (p == PEEKED_BUFFERED) {
builder.append( peekedString );
writer.value( peekedString );
}
peeked = PEEKED_NONE;
} while (count != 0);

return builder.toString();
writer.close();
return writer.getOutput();
}

@Override
Expand Down Expand Up @@ -1650,5 +1645,36 @@ public Number nextNumber()
peeked = PEEKED_NONE;
return result;
}

@Override
public JavaScriptObject nextJavaScriptObject( boolean useSafeEval ) {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
}

switch (p) {
case PEEKED_BEGIN_OBJECT:
case PEEKED_BEGIN_ARRAY:
JavaScriptObject result;
int peekStack = stack.get(stackSize - 1);
if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
// start of the document
String toEval = in.getInput();
result = useSafeEval ? JsonUtils.safeEval( toEval ) : JsonUtils.unsafeEval( toEval );
// we read everything, we move the pointer to the end of the document
pos = toEval.length();
limit = toEval.length();
peeked = PEEKED_NONE;
} else {
String toEval = nextValue();
result = useSafeEval ? JsonUtils.safeEval( toEval ) : JsonUtils.unsafeEval( toEval );
}
return result;
default:
throw new IllegalStateException("Expected an array or object to evaluate a JavaScriptObject but was " + peek()
+ " at line " + getLineNumber() + " column " + getColumnNumber());
}
}
}
//@formatter:on
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.logging.Logger;

import com.github.nmorel.gwtjackson.client.exception.JsonSerializationException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayInteger;

/**
Expand Down Expand Up @@ -438,6 +439,21 @@ public DefaultJsonWriter value( Number value ) {
return this;
}

@Override
public DefaultJsonWriter value( JavaScriptObject value ) {
if (value == null) {
return nullValue();
}
writeDeferredName();
beforeValue(false);
out.append(stringify( value ));
return this;
}

private native String stringify( JavaScriptObject jso ) /*-{
return JSON.stringify(jso);
}-*/;

@Override
public DefaultJsonWriter rawValue( Object value ) {
if (value == null) {
Expand Down
Loading

0 comments on commit 2484905

Please sign in to comment.