YAML prototype. Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/ab8f0faa Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/ab8f0faa Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/ab8f0faa
Branch: refs/heads/master Commit: ab8f0faaa862c84e344329725bccbb6a4b86ae15 Parents: b89cb98 Author: JamesBognar <jamesbog...@apache.org> Authored: Sun Sep 24 14:34:21 2017 -0400 Committer: JamesBognar <jamesbog...@apache.org> Committed: Sun Sep 24 14:34:21 2017 -0400 ---------------------------------------------------------------------- .../apache/juneau/utils/KeywordStoreTest.java | 4 - .../juneau/yaml/proto/CommonParserTest.java | 180 ++++ .../apache/juneau/yaml/proto/CommonTest.java | 339 ++++++ .../juneau/yaml/proto/YamlParserTest.java | 326 ++++++ .../org/apache/juneau/yaml/proto/YamlTest.java | 215 ++++ .../org/apache/juneau/internal/KeywordSet.java | 57 +- .../org/apache/juneau/internal/SystemUtils.java | 71 ++ .../apache/juneau/yaml/proto/YamlClassMeta.java | 45 + .../apache/juneau/yaml/proto/YamlParser.java | 163 +++ .../juneau/yaml/proto/YamlParserBuilder.java | 465 ++++++++ .../juneau/yaml/proto/YamlParserContext.java | 61 ++ .../juneau/yaml/proto/YamlParserSession.java | 669 ++++++++++++ .../juneau/yaml/proto/YamlSerializer.java | 146 +++ .../yaml/proto/YamlSerializerBuilder.java | 658 ++++++++++++ .../yaml/proto/YamlSerializerContext.java | 130 +++ .../yaml/proto/YamlSerializerSession.java | 244 +++++ .../apache/juneau/yaml/proto/YamlWriter.java | 288 +++++ .../juneau/yaml/proto/annotation/Yaml.java | 36 + .../juneau/yaml/proto/annotation/package.html | 41 + .../yaml/proto/doc-files/Example_HTML.png | Bin 0 -> 35528 bytes .../yaml/proto/doc-files/Example_JSON.png | Bin 0 -> 26954 bytes .../yaml/proto/doc-files/Example_JSONSchema.png | Bin 0 -> 34114 bytes .../yaml/proto/doc-files/Example_JSONSimple.png | Bin 0 -> 30920 bytes .../org/apache/juneau/yaml/proto/package.html | 1005 ++++++++++++++++++ 24 files changed, 5087 insertions(+), 56 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/KeywordStoreTest.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/KeywordStoreTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/KeywordStoreTest.java index 89ac7b4..a61eb1d 100755 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/KeywordStoreTest.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/KeywordStoreTest.java @@ -38,9 +38,5 @@ public class KeywordStoreTest { assertFalse(ks.contains("|a")); assertFalse(ks.contains("Aa")); assertFalse(ks.contains("aA")); - - for (String s : new String[]{"a","aA","Aa","a|","|a"}) { - try { ks = new KeywordSet(s); fail(); } catch (IllegalArgumentException e) {} - } } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/CommonParserTest.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/CommonParserTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/CommonParserTest.java new file mode 100755 index 0000000..e8dd392 --- /dev/null +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/CommonParserTest.java @@ -0,0 +1,180 @@ +// *************************************************************************************************************************** +// * 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 org.apache.juneau.yaml.proto; + +import static org.junit.Assert.*; + +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.parser.*; +import org.junit.*; + +@SuppressWarnings({"rawtypes","serial","javadoc"}) +public class CommonParserTest { + + //==================================================================================================== + // testFromSerializer + //==================================================================================================== + @Test + public void testFromSerializer() throws Exception { + ReaderParser p = new YamlParserBuilder().beanDictionary(A1.class).build(); + + Map m = null; + m = (Map)p.parse("{a:1}", Object.class); + assertEquals(1, m.get("a")); + m = (Map)p.parse("{a:1,b:\"foo bar\"}", Object.class); + assertEquals(1, m.get("a")); + assertEquals("foo bar", m.get("b")); + m = (Map)p.parse("{a:1,b:\"foo bar\",c:false}", Object.class); + assertEquals(1, m.get("a")); + assertEquals(false, m.get("c")); + m = (Map)p.parse(" { a : 1 , b : 'foo' , c : false } ", Object.class); + assertEquals(1, m.get("a")); + assertEquals("foo", m.get("b")); + assertEquals(false, m.get("c")); + + m = (Map)p.parse("{x:\"org.apache.juneau.test.Person\",addresses:[{x:\"org.apache.juneau.test.Address\",city:\"city A\",state:\"state A\",street:\"street A\",zip:12345}]}", Object.class); + assertEquals("org.apache.juneau.test.Person", m.get("x")); + List l = (List)m.get("addresses"); + assertNotNull(l); + m = (Map)l.get(0); + assertNotNull(m); + assertEquals("org.apache.juneau.test.Address", m.get("x")); + assertEquals("city A", m.get("city")); + assertEquals("state A", m.get("state")); + assertEquals("street A", m.get("street")); + assertEquals(12345, m.get("zip")); + + ObjectList jl = (ObjectList)p.parse("[{attribute:'value'},{attribute:'value'}]", Object.class); + assertEquals("value", jl.getObjectMap(0).getString("attribute")); + assertEquals("value", jl.getObjectMap(1).getString("attribute")); + + // Verify that all the following return null. + assertNull(p.parse((CharSequence)null, Object.class)); + assertNull(p.parse("", Object.class)); + assertNull(p.parse(" ", Object.class)); + assertNull(p.parse(" \t", Object.class)); + assertNull(p.parse(" /*foo*/", Object.class)); + assertNull(p.parse(" /*foo*/ ", Object.class)); + assertNull(p.parse(" //foo ", Object.class)); + + try { + jl = (ObjectList)p.parse("[{attribute:'value'},{attribute:'value'}]", Object.class); + assertEquals("value", jl.getObjectMap(0).getString("attribute")); + assertEquals("value", jl.getObjectMap(1).getString("attribute")); + } catch (Exception e) { + fail(e.getLocalizedMessage()); + } + + A1 b = new A1(); + A2 tl = new A2(); + tl.add(new A3("name0","value0")); + tl.add(new A3("name1","value1")); + b.list = tl; + String json = new YamlSerializerBuilder().addBeanTypeProperties(true).beanDictionary(A1.class).build().serialize(b); + b = (A1)p.parse(json, Object.class); + assertEquals("value1", b.list.get(1).value); + + json = YamlSerializer.DEFAULT.serialize(b); + b = p.parse(json, A1.class); + assertEquals("value1", b.list.get(1).value); + } + + @Bean(typeName="A1") + public static class A1 { + public A2 list; + } + + public static class A2 extends LinkedList<A3> { + } + + public static class A3 { + public String name, value; + public A3(){} + public A3(String name, String value) { + this.name = name; + this.value = value; + } + } + + //==================================================================================================== + // Correct handling of unknown properties. + //==================================================================================================== + @Test + public void testCorrectHandlingOfUnknownProperties() throws Exception { + ReaderParser p = new YamlParserBuilder().ignoreUnknownBeanProperties(true).build(); + B b; + + String in = "{a:1,unknown:3,b:2}"; + b = p.parse(in, B.class); + assertEquals(b.a, 1); + assertEquals(b.b, 2); + + try { + p = YamlParser.DEFAULT; + p.parse(in, B.class); + fail("Exception expected"); + } catch (ParseException e) {} + } + + public static class B { + public int a, b; + } + + //==================================================================================================== + // Writing to Collection properties with no setters. + //==================================================================================================== + @Test + public void testCollectionPropertiesWithNoSetters() throws Exception { + YamlParser p = YamlParser.DEFAULT; + String json = "{ints:[1,2,3],beans:[{a:1,b:2}]}"; + C t = p.parse(json, C.class); + assertEquals(t.getInts().size(), 3); + assertEquals(t.getBeans().get(0).b, 2); + } + + public static class C { + private Collection<Integer> ints = new LinkedList<Integer>(); + private List<B> beans = new LinkedList<B>(); + public Collection<Integer> getInts() { + return ints; + } + public List<B> getBeans() { + return beans; + } + } + + //==================================================================================================== + // Parser listeners. + //==================================================================================================== + @Test + public void testParserListeners() throws Exception { + YamlParser p = new YamlParserBuilder().ignoreUnknownBeanProperties(true).listener(MyParserListener.class).build(); + + String json = "{a:1,unknownProperty:\"/foo\",b:2}"; + p.parse(json, B.class); + assertEquals(1, MyParserListener.events.size()); + assertEquals("unknownProperty,1,5", MyParserListener.events.get(0)); + } + + public static class MyParserListener extends ParserListener { + final static List<String> events = new LinkedList<String>(); + + @Override /* ParserListener */ + public <T> void onUnknownBeanProperty(ParserSession session, ParserPipe pipe, String propertyName, Class<T> beanClass, T bean, int line, int col) { + events.add(propertyName + "," + line + "," + col); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/CommonTest.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/CommonTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/CommonTest.java new file mode 100755 index 0000000..a6e6e7b --- /dev/null +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/CommonTest.java @@ -0,0 +1,339 @@ +// *************************************************************************************************************************** +// * 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 org.apache.juneau.yaml.proto; + +import static org.apache.juneau.TestUtils.*; +import static org.junit.Assert.*; + +import java.net.*; +import java.net.URI; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.utils.*; +import org.junit.*; + +@SuppressWarnings({"serial","javadoc"}) +@Ignore +public class CommonTest { + + //==================================================================================================== + // Trim nulls from beans + //==================================================================================================== + @Test + public void testTrimNullsFromBeans() throws Exception { + YamlSerializerBuilder s = new YamlSerializerBuilder().simple(); + YamlParser p = YamlParser.DEFAULT; + A t1 = A.create(), t2; + + s.trimNullProperties(false); + String r = s.build().serialize(t1); + assertEquals("{s1:null,s2:'s2'}", r); + t2 = p.parse(r, A.class); + assertEqualObjects(t1, t2); + + s.trimNullProperties(true); + r = s.build().serialize(t1); + assertEquals("{s2:'s2'}", r); + t2 = p.parse(r, A.class); + assertEqualObjects(t1, t2); + } + + public static class A { + public String s1, s2; + + public static A create() { + A t = new A(); + t.s2 = "s2"; + return t; + } + } + + //==================================================================================================== + // Trim empty maps + //==================================================================================================== + @Test + public void testTrimEmptyMaps() throws Exception { + YamlSerializerBuilder s = new YamlSerializerBuilder().simple(); + YamlParser p = YamlParser.DEFAULT; + B t1 = B.create(), t2; + String r; + + s.trimEmptyMaps(false); + r = s.build().serialize(t1); + assertEquals("{f1:{},f2:{f2a:null,f2b:{s2:'s2'}}}", r); + t2 = p.parse(r, B.class); + assertEqualObjects(t1, t2); + + s.trimEmptyMaps(true); + r = s.build().serialize(t1); + assertEquals("{f2:{f2a:null,f2b:{s2:'s2'}}}", r); + t2 = p.parse(r, B.class); + assertNull(t2.f1); + } + + public static class B { + public TreeMap<String,A> f1, f2; + + public static B create() { + B t = new B(); + t.f1 = new TreeMap<String,A>(); + t.f2 = new TreeMap<String,A>(){{put("f2a",null);put("f2b",A.create());}}; + return t; + } + } + + //==================================================================================================== + // Trim empty lists + //==================================================================================================== + @Test + public void testTrimEmptyLists() throws Exception { + YamlSerializerBuilder s = new YamlSerializerBuilder().simple(); + YamlParser p = YamlParser.DEFAULT; + C t1 = C.create(), t2; + String r; + + s.trimEmptyCollections(false); + r = s.build().serialize(t1); + assertEquals("{f1:[],f2:[null,{s2:'s2'}]}", r); + t2 = p.parse(r, C.class); + assertEqualObjects(t1, t2); + + s.trimEmptyCollections(true); + r = s.build().serialize(t1); + assertEquals("{f2:[null,{s2:'s2'}]}", r); + t2 = p.parse(r, C.class); + assertNull(t2.f1); + } + + public static class C { + public List<A> f1, f2; + + public static C create() { + C t = new C(); + t.f1 = new AList<A>(); + t.f2 = new AList<A>().append(null).append(A.create()); + return t; + } + } + + //==================================================================================================== + // Trim empty arrays + //==================================================================================================== + @Test + public void testTrimEmptyArrays() throws Exception { + YamlSerializerBuilder s = new YamlSerializerBuilder().simple(); + YamlParser p = YamlParser.DEFAULT; + D t1 = D.create(), t2; + String r; + + s.trimEmptyCollections(false); + r = s.build().serialize(t1); + assertEquals("{f1:[],f2:[null,{s2:'s2'}]}", r); + t2 = p.parse(r, D.class); + assertEqualObjects(t1, t2); + + s.trimEmptyCollections(true); + r = s.build().serialize(t1); + assertEquals("{f2:[null,{s2:'s2'}]}", r); + t2 = p.parse(r, D.class); + assertNull(t2.f1); + } + + public static class D { + public A[] f1, f2; + + public static D create() { + D t = new D(); + t.f1 = new A[]{}; + t.f2 = new A[]{null, A.create()}; + return t; + } + } + + //==================================================================================================== + // @BeanProperty.properties annotation. + //==================================================================================================== + @Test + public void testBeanPropertyProperies() throws Exception { + YamlSerializer s = YamlSerializer.DEFAULT; + E1 t = new E1(); + String r; + + r = s.serialize(t); + assertEquals("{x1:{f1:1},x2:{f1:1},x3:[{f1:1}],x4:[{f1:1}],x5:[{f1:1}],x6:[{f1:1}]}", r); + } + + public static class E1 { + @BeanProperty(properties="f1") public E2 x1 = new E2(); + @BeanProperty(properties="f1") public Map<String,Integer> x2 = new AMap<String,Integer>().append("f1",1).append("f2",2); + @BeanProperty(properties="f1") public E2[] x3 = {new E2()}; + @BeanProperty(properties="f1") public List<E2> x4 = new AList<E2>().append(new E2()); + @BeanProperty(properties="f1") public ObjectMap[] x5 = {new ObjectMap().append("f1",1).append("f2",2)}; + @BeanProperty(properties="f1") public List<ObjectMap> x6 = new AList<ObjectMap>().append(new ObjectMap().append("f1",1).append("f2",2)); + } + + public static class E2 { + public int f1 = 1; + public int f2 = 2; + } + + //==================================================================================================== + // @BeanProperty.properties annotation on list of beans. + //==================================================================================================== + @Test + public void testBeanPropertyProperiesOnListOfBeans() throws Exception { + YamlSerializer s = YamlSerializer.DEFAULT; + List<F> l = new LinkedList<F>(); + F t = new F(); + t.x1.add(new F()); + l.add(t); + String json = s.serialize(l); + assertEquals("[{x1:[{x2:2}],x2:2}]", json); + } + + public static class F { + @BeanProperty(properties="x2") public List<F> x1 = new LinkedList<F>(); + public int x2 = 2; + } + + //==================================================================================================== + // Test that URLs and URIs are serialized and parsed correctly. + //==================================================================================================== + @Test + public void testURIAttr() throws Exception { + YamlSerializer s = YamlSerializer.DEFAULT; + YamlParser p = YamlParser.DEFAULT; + + G t = new G(); + t.uri = new URI("http://uri"); + t.f1 = new URI("http://f1"); + t.f2 = new URL("http://f2"); + + String json = s.serialize(t); + t = p.parse(json, G.class); + assertEquals("http://uri", t.uri.toString()); + assertEquals("http://f1", t.f1.toString()); + assertEquals("http://f2", t.f2.toString()); + } + + public static class G { + public URI uri; + public URI f1; + public URL f2; + } + + + //==================================================================================================== + // Recursion + //==================================================================================================== + @Test + public void testRecursion() throws Exception { + YamlSerializerBuilder s = new YamlSerializerBuilder().simple(); + + R1 r1 = new R1(); + R2 r2 = new R2(); + R3 r3 = new R3(); + r1.r2 = r2; + r2.r3 = r3; + r3.r1 = r1; + + // No recursion detection + try { + s.build().serialize(r1); + fail("Exception expected!"); + } catch (Exception e) { + String msg = e.getLocalizedMessage(); + assertTrue(msg.contains("It's recommended you use the SerializerContext.SERIALIZER_detectRecursions setting to help locate the loop.")); + } + + // Recursion detection, no ignore + s.detectRecursions(true); + try { + s.build().serialize(r1); + fail("Exception expected!"); + } catch (Exception e) { + String msg = e.getLocalizedMessage(); + assertTrue(msg.contains("[0]root:org.apache.juneau.json.CommonTest$R1")); + assertTrue(msg.contains("->[1]r2:org.apache.juneau.json.CommonTest$R2")); + assertTrue(msg.contains("->[2]r3:org.apache.juneau.json.CommonTest$R3")); + assertTrue(msg.contains("->[3]r1:org.apache.juneau.json.CommonTest$R1")); + } + + s.ignoreRecursions(true); + assertEquals("{name:'foo',r2:{name:'bar',r3:{name:'baz'}}}", s.build().serialize(r1)); + } + + public static class R1 { + public String name = "foo"; + public R2 r2; + } + public static class R2 { + public String name = "bar"; + public R3 r3; + } + public static class R3 { + public String name = "baz"; + public R1 r1; + } + + //==================================================================================================== + // Basic bean + //==================================================================================================== + @Test + public void testBasicBean() throws Exception { + YamlSerializer s = new YamlSerializerBuilder().simple().trimNullProperties(false).sortProperties(true).build(); + + J a = new J(); + a.setF1("J"); + a.setF2(100); + a.setF3(true); + assertEquals("C1", "{f1:'J',f2:100,f3:true}", s.serialize(a)); + } + + public static class J { + private String f1 = null; + private int f2 = -1; + private boolean f3 = false; + + public String getF1() { + return this.f1; + } + + public void setF1(String f1) { + this.f1 = f1; + } + + public int getF2() { + return this.f2; + } + + public void setF2(int f2) { + this.f2 = f2; + } + + public boolean isF3() { + return this.f3; + } + + public void setF3(boolean f3) { + this.f3 = f3; + } + + @Override /* Object */ + public String toString() { + return ("J(f1: " + this.getF1() + ", f2: " + this.getF2() + ")"); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/YamlParserTest.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/YamlParserTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/YamlParserTest.java new file mode 100755 index 0000000..0b0c667 --- /dev/null +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/YamlParserTest.java @@ -0,0 +1,326 @@ +// *************************************************************************************************************************** +// * 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 org.apache.juneau.yaml.proto; + +import static org.junit.Assert.*; + +import org.apache.juneau.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.junit.*; + +@SuppressWarnings({"javadoc"}) +@Ignore +public class YamlParserTest { + + private static final YamlParser p = YamlParser.DEFAULT; + private static final YamlParser sp = YamlParser.DEFAULT_STRICT; + + + //==================================================================================================== + // Test invalid input + //==================================================================================================== + @Test + public void testInvalidYaml() { + try { + p.parse("{\na:1,\nb:xxx\n}", Object.class); + fail("Exception expected."); + } catch (ParseException e) {} + } + + @Test + public void testNonExistentAttribute() throws Exception { + String json = "{foo:,bar:}"; + ObjectMap m = p.parse(json, ObjectMap.class); + assertEquals("{foo:null,bar:null}", m.toString()); + } + + @Test + public void testNonStringAsString() throws Exception { + String json = "123"; + String s; + + // Strict mode does not allow unquoted values. + try { + sp.parse(json, String.class); + fail("Exception expected"); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Did not find quote character")); + } + + s = p.parse(json, String.class); + assertEquals("123", s); + + json = " 123 "; + // Strict mode does not allow unquoted values. + try { + sp.parse(json, String.class); + fail("Exception expected"); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Did not find quote character")); + } + + s = p.parse(json, String.class); + assertEquals("123", s); + + json = "{\"fa\":123}"; + try { + sp.parse(json, A.class); + fail("Exception expected"); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Did not find quote character")); + } + + A a = p.parse(json, A.class); + assertEquals("123", a.fa); + + json = " { \"fa\" : 123 } "; + try { + sp.parse(json, A.class); + fail("Exception expected"); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Did not find quote character")); + } + + a = p.parse(json, A.class); + assertEquals("123", a.fa); + + json = "'123'"; + try { + sp.parse(json, String.class); + fail("Exception expected"); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Invalid quote character")); + } + } + + public static class A { + public String fa; + } + + @Test + public void testStrictMode() throws Exception { + YamlParser p = sp; + + // Missing attribute values. + String json = "{\"foo\":,\"bar\":}"; + try { + p.parse(json, ObjectMap.class); + fail("Exception expected"); + } catch (ParseException e) { + assertEquals("Parse exception occurred at {currentClass:'Object',line:1,column:7}. Missing value detected.", e.getRootCause().getMessage()); + } + + // Single quoted values. + json = "{\"foo\":'bar'}"; + try { + p.parse(json, ObjectMap.class); + fail("Exception expected"); + } catch (ParseException e) { + assertEquals("Parse exception occurred at {currentClass:'Object',line:1,column:8}. Invalid quote character \"'\" being used.", e.getRootCause().getMessage()); + } + + // Single quoted attribute name. + json = "{'foo':\"bar\"}"; + try { + p.parse(json, ObjectMap.class); + fail("Exception expected"); + } catch (ParseException e) { + assertEquals("Parse exception occurred at {currentClass:'ObjectMap<String,Object>',line:1,column:2}. Invalid quote character \"'\" being used.", e.getRootCause().getMessage()); + } + + // Unquoted attribute name. + json = "{foo:\"bar\"}"; + try { + p.parse(json, ObjectMap.class); + fail("Exception expected"); + } catch (ParseException e) { + assertEquals("Parse exception occurred at {currentClass:'ObjectMap<String,Object>',line:1,column:1}. Unquoted attribute detected.", e.getRootCause().getMessage()); + } + + // Concatenated string + json = "{\"foo\":\"bar\"+\"baz\"}"; + try { + p.parse(json, ObjectMap.class); + fail("Exception expected"); + } catch (ParseException e) { + assertEquals("Parse exception occurred at {currentClass:'Object',line:1,column:12}. String concatenation detected.", e.getRootCause().getMessage()); + } + + // Concatenated string 2 + json = "{\"foo\":\"bar\" + \"baz\"}"; + try { + p.parse(json, ObjectMap.class); + fail("Exception expected"); + } catch (ParseException e) { + assertEquals("Parse exception occurred at {currentClass:'Object',line:1,column:13}. String concatenation detected.", e.getRootCause().getMessage()); + } + + json = "{\"foo\":/*comment*/\"bar\"}"; + try { + p.parse(json, ObjectMap.class); + fail("Exception expected"); + } catch (ParseException e) { + assertEquals("Parse exception occurred at {currentClass:'ObjectMap<String,Object>',line:1,column:8}. Javascript comment detected.", e.getRootCause().getMessage()); + } + } + + /** + * JSON numbers and booleans should be representable as strings and converted accordingly. + */ + @Test + public void testPrimitivesAsStrings() throws Exception { + String json; + ReaderParser p = YamlParser.DEFAULT; + WriterSerializer s = YamlSerializer.DEFAULT; + + json = "{f1:'1',f2:'1',f3:'true',f4:'true',f5:'1',f6:'1',f7:'1',f8:'1',f9:'1',f10:'1'}"; + B b = p.parse(json, B.class); + assertEquals("{f1:1,f2:1,f3:true,f4:true,f5:1.0,f6:1.0,f7:1,f8:1,f9:1,f10:1}", s.toString(b)); + + json = "{f1:'',f2:'',f3:'',f4:'',f5:'',f6:'',f7:'',f8:'',f9:'',f10:''}"; + b = p.parse(json, B.class); + assertEquals("{f1:0,f2:0,f3:false,f4:false,f5:0.0,f6:0.0,f7:0,f8:0,f9:0,f10:0}", s.toString(b)); + } + + public static class B { + public int f1; + public Integer f2; + public boolean f3; + public Boolean f4; + public float f5; + public Float f6; + public long f7; + public Long f8; + public byte f9; + public Byte f10; + } + + //==================================================================================================== + // testInvalidYamlNumbers + // Lax parser allows octal and hexadecimal numbers. Strict parser does not. + //==================================================================================================== + @Test + public void testInvalidYamlNumbers() throws Exception { + YamlParser p1 = YamlParser.DEFAULT; + YamlParser p2 = YamlParser.DEFAULT_STRICT; + Number r; + + // Lax allows blank strings interpreted as 0, strict does not. + String s = "\"\""; + r = p1.parse(s, Number.class); + assertEquals(0, r.intValue()); + assertTrue(r instanceof Integer); + try { + r = p2.parse(s, Number.class); + fail("Exception expected"); + } catch (ParseException e) { + assertTrue(e.getMessage().contains("Invalid JSON number")); + } + + // Either should allow 0 or -0. + s = "0"; + r = p1.parse(s, Number.class); + assertEquals(0, r.intValue()); + assertTrue(r instanceof Integer); + r = p2.parse(s, Number.class); + assertEquals(0, r.intValue()); + assertTrue(r instanceof Integer); + + s = "-0"; + r = p1.parse(s, Number.class); + assertEquals(0, r.intValue()); + assertTrue(r instanceof Integer); + r = p2.parse(s, Number.class); + assertEquals(0, r.intValue()); + assertTrue(r instanceof Integer); + + // Lax allows 0123 and -0123, strict does not. + s = "0123"; + r = p1.parse(s, Number.class); + assertEquals(0123, r.intValue()); + assertTrue(r instanceof Integer); + try { + r = p2.parse(s, Number.class); + fail("Exception expected"); + } catch (ParseException e) { + assertTrue(e.getMessage().contains("Invalid JSON number")); + } + s = "-0123"; + r = p1.parse(s, Number.class); + assertEquals(-0123, r.intValue()); + assertTrue(r instanceof Integer); + try { + r = p2.parse(s, Number.class); + fail("Exception expected"); + } catch (ParseException e) { + assertTrue(e.getMessage().contains("Invalid JSON number")); + } + + // Lax allows 0x123 and -0x123, strict does not. + s = "0x123"; + r = p1.parse(s, Number.class); + assertEquals(0x123, r.intValue()); + assertTrue(r instanceof Integer); + try { + r = p2.parse(s, Number.class); + fail("Exception expected"); + } catch (ParseException e) { + assertTrue(e.getMessage().contains("Invalid JSON number")); + } + s = "-0x123"; + r = p1.parse(s, Number.class); + assertEquals(-0x123, r.intValue()); + assertTrue(r instanceof Integer); + try { + r = p2.parse(s, Number.class); + fail("Exception expected"); + } catch (ParseException e) { + assertTrue(e.getMessage().contains("Invalid JSON number")); + } + } + + //==================================================================================================== + // testUnquotedStrings + // Lax parser allows unquoted strings if POJO can be converted from a string. + //==================================================================================================== + @Test + public void testUnquotedStrings() throws Exception { + YamlParser p1 = YamlParser.DEFAULT; + YamlParser p2 = YamlParser.DEFAULT_STRICT; + + String s = "foobar"; + C c = p1.parse(s, C.class); + assertEquals("f=foobar", c.toString()); + + try { + p2.parse(s, C.class); + fail("Exception expected"); + } catch (ParseException e) { + // OK + } + } + + public static class C { + String f; + public static C valueOf(String s) { + C c = new C(); + c.f = s; + return c; + } + @Override /* Object */ + public String toString() { + return "f="+f; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/YamlTest.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/YamlTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/YamlTest.java new file mode 100755 index 0000000..cf74bf6 --- /dev/null +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/yaml/proto/YamlTest.java @@ -0,0 +1,215 @@ +// *************************************************************************************************************************** +// * 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 org.apache.juneau.yaml.proto; + +import org.junit.*; + +@SuppressWarnings({"javadoc"}) +public class YamlTest { + + //==================================================================================================== + // testBasic + //==================================================================================================== + @Test + public void testBasic() throws Exception { +// Map<String,Object> m = new LinkedHashMap<String,Object>(); +// List<Object> l = new LinkedList<Object>(); +// +// WriterSerializer s1 = new YamlSerializerBuilder().simple().trimNullProperties(false).build(); +// WriterSerializer s2 = new YamlSerializerBuilder().simple().trimNullProperties(false).quoteChar('"').build(); +// String r; +// +// // Null keys and values +// m.clear(); +// m.put(null, null); +// m.put("aaa", "bbb"); +// assertEquals("A1", "~: ~,\naaa: bbb\n", s1.serialize(m)); +// +// // Escapes. +// // String = ["] +// m.clear(); +// m.put("x", "[\"]"); +// assertEquals("{x:\"[\\\"]\"}", s2.serialize(m)); +// // String = [\"] +// // JSON = {x:"\\\""} +// m.clear(); +// m.put("x", "[\\\"]"); +// assertEquals("{x:\"[\\\\\\\"]\"}", s2.serialize(m)); +// +// // String = [\w[\w\-\.]{3,}\w] +// // JSON = {x:"\\w[\\w\\-\\.]{3,}\\w"} +// m.clear(); +// r = "\\w[\\w\\-\\.]{3,}\\w"; +// m.put("x", r); +// assertEquals("{x:\"\\\\w[\\\\w\\\\-\\\\.]{3,}\\\\w\"}", s2.serialize(m)); +// assertEquals(r, new ObjectMap(s2.serialize(m)).getString("x")); +// +// // String = [foo\bar] +// // JSON = {x:"foo\\bar"} +// m.clear(); +// m.put("x", "foo\\bar"); +// assertEquals("{x:\"foo\\\\bar\"}", s2.serialize(m)); +// +// m.clear(); +// m.put("null", null); +// m.put("aaa", "bbb"); +// assertEquals("A2", "{'null':null,aaa:'bbb'}", s1.serialize(m)); +// +// m.clear(); +// m.put(null, "null"); +// m.put("aaa", "bbb"); +// assertEquals("A3", "{null:'null',aaa:'bbb'}", s1.serialize(m)); +// +// // Arrays +// m.clear(); +// l.clear(); +// m.put("J", "f1"); +// m.put("B", "b"); +// m.put("C", "c"); +// l.add("1"); +// l.add("2"); +// l.add("3"); +// Object o = new Object[] { m, l }; +// Object o2 = new Object[] { o, "foo", "bar", new Integer(1), new Boolean(false), new Float(1.2), null }; +// assertEquals("K1", "[[{J:'f1',B:'b',C:'c'},['1','2','3']],'foo','bar',1,false,1.2,null]", s1.serialize(o2)); + } + +// @Test +// public void testReservedKeywordAttributes() throws Exception { +// Map<String,Object> m = new LinkedHashMap<String,Object>(); +// +// // Keys with reserved names. +// for (String attr : new String[]{"","true","false","null","try","123","1x","-123",".123"}) { +// m.clear(); +// m.put(attr,1); +// assertObjectEquals("{'"+attr+"':1}", m); +// } +// } +// +// //==================================================================================================== +// // Validate various backslashes in strings. +// //==================================================================================================== +// @Test +// public void testBackslashesInStrings() throws Exception { +// YamlSerializer s = new YamlSerializerBuilder().simple().trimNullProperties(false).quoteChar('"').build(); +// String r, r2; +// +// // [\\] +// r = "\\"; +// r2 = s.serialize(r); +// assertEquals(r2, "\"\\\\\""); +// assertEquals(YamlParser.DEFAULT.parse(r2, Object.class), r); +// +// // [\b\f\n\t] +// r = "\b\f\n\t"; +// r2 = s.serialize(r); +// assertEquals("\"\\b\\f\\n\\t\"", r2); +// assertEquals(r, YamlParser.DEFAULT.parse(r2, Object.class)); +// +// // Special JSON case: Forward slashes can OPTIONALLY be escaped. +// // [\/] +// assertEquals(YamlParser.DEFAULT.parse("\"\\/\"", Object.class), "/"); +// +// // Unicode +// r = "\u1234\u1ABC\u1abc"; +// r2 = s.serialize(r); +// assertEquals("\"\u1234\u1ABC\u1abc\"", r2); +// +// assertEquals("\u1234", YamlParser.DEFAULT.parse("\"\\u1234\"", Object.class)); +// } +// +// //==================================================================================================== +// // Indentation +// //==================================================================================================== +// @Test +// public void testIndentation() throws Exception { +// ObjectMap m = new ObjectMap("{J:{B:['c',{D:'e'},['f',{G:'h'},1,false]]},I:'j'}"); +// String e = "" +// + "{" +// + "\n J: {" +// + "\n B: [" +// + "\n 'c'," +// + "\n {" +// + "\n D: 'e'" +// + "\n }," +// + "\n [" +// + "\n 'f'," +// + "\n {" +// + "\n G: 'h'" +// + "\n }," +// + "\n 1," +// + "\n false" +// + "\n ]" +// + "\n ]" +// + "\n }," +// + "\n I: 'j'" +// + "\n}"; +// assertEquals(e, YamlSerializer.DEFAULT.serialize(m)); +// } +// +// //==================================================================================================== +// // Escaping double quotes +// //==================================================================================================== +// @Test +// public void testEscapingDoubleQuotes() throws Exception { +// YamlSerializer s = YamlSerializer.DEFAULT; +// String r = s.serialize(new ObjectMap().append("f1", "x'x\"x")); +// assertEquals("{\"f1\":\"x'x\\\"x\"}", r); +// YamlParser p = YamlParser.DEFAULT; +// assertEquals("x'x\"x", p.parse(r, ObjectMap.class).getString("f1")); +// } +// +// //==================================================================================================== +// // Escaping single quotes +// //==================================================================================================== +// @Test +// public void testEscapingSingleQuotes() throws Exception { +// YamlSerializer s = YamlSerializer.DEFAULT; +// String r = s.serialize(new ObjectMap().append("f1", "x'x\"x")); +// assertEquals("{f1:'x\\'x\"x'}", r); +// YamlParser p = YamlParser.DEFAULT; +// assertEquals("x'x\"x", p.parse(r, ObjectMap.class).getString("f1")); +// } +// +// //==================================================================================================== +// // testSubclassedList +// //==================================================================================================== +// @Test +// public void testSubclassedList() throws Exception { +// YamlSerializer s = YamlSerializer.DEFAULT; +// Map<String,Object> o = new HashMap<String,Object>(); +// o.put("c", new C()); +// assertEquals("{\"c\":[]}", s.serialize(o)); +// } +// +// public static class C extends LinkedList<String> { +// } +// +// //==================================================================================================== +// // testEscapeSolidus +// //==================================================================================================== +// @Test +// public void testEscapeSolidus() throws Exception { +// YamlSerializer s = new YamlSerializerBuilder().escapeSolidus(false).build(); +// String r = s.serialize("foo/bar"); +// assertEquals("\"foo/bar\"", r); +// r = YamlParser.DEFAULT.parse(r, String.class); +// assertEquals("foo/bar", r); +// +// s = new YamlSerializerBuilder().escapeSolidus(true).build(); +// r = s.serialize("foo/bar"); +// assertEquals("\"foo\\/bar\"", r); +// r = YamlParser.DEFAULT.parse(r, String.class); +// assertEquals("foo/bar", r); +// } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/KeywordSet.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/KeywordSet.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/KeywordSet.java index 15ebd33..b2f79d7 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/KeywordSet.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/KeywordSet.java @@ -12,22 +12,13 @@ // *************************************************************************************************************************** package org.apache.juneau.internal; -import static org.apache.juneau.internal.ThrowableUtils.*; +import java.util.*; /** * Stores a set of language keywords for quick lookup. - * - * <p> - * Keywords must be: - * <ul class='spaced-list'> - * <li> - * 2 or more characters in length. - * <li> - * Lowercase ASCII. - * </ul> */ public final class KeywordSet { - final char[][][][] store; + final String[] store; /** * Constructor. @@ -35,28 +26,8 @@ public final class KeywordSet { * @param keywords The list of keywords. */ public KeywordSet(String... keywords) { - this.store = new char[26][][][]; - - for (String keyword : keywords) { - if (keyword.length() < 2) - illegalArg("Invalid keyword '{0}' passed to KeywordStore.", keyword); - int c0 = keyword.charAt(0) - 'a'; - int c1 = keyword.charAt(1) - 'a'; - if (c0 < 0 || c0 > 25 || c1 < 0 || c1 > 25) - illegalArg("Invalid keyword '{0}' passed to KeywordStore.", keyword); - if (this.store[c0] == null) - this.store[c0] = new char[26][][]; - char[][][] x1 = this.store[c0]; - char[][] x2; - if (x1[c1] == null) - x2 = new char[1][]; - else { - x2 = new char[x1[c1].length+1][]; - System.arraycopy(x1[c1], 0, x2, 0, x1[c1].length); - } - x2[x2.length-1] = keyword.toCharArray(); - x1[c1] = x2; - } + this.store = keywords; + Arrays.sort(store); } /** @@ -68,24 +39,6 @@ public final class KeywordSet { public boolean contains(String s) { if (s == null || s.length() < 2) return false; - int c0 = s.charAt(0) - 'a', c1 = s.charAt(1) - 'a'; - if (c0 < 0 || c0 > 25 || c1 < 0 || c1 > 25) - return false; - char[][][] x1 = store[c0]; - if (x1 == null) - return false; - char[][] x2 = x1[c1]; - if (x2 == null) - return false; - for (int i = 0; i < x2.length; i++) { - char[] keyword = x2[i]; - if (keyword.length == s.length()) { - for (int j = 0; j < keyword.length; j++) - if (keyword[j] != s.charAt(j)) - return false; - return true; - } - } - return false; + return Arrays.binarySearch(store, s) >= 0; } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/SystemUtils.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/SystemUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/SystemUtils.java new file mode 100644 index 0000000..5f7a6b9 --- /dev/null +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/SystemUtils.java @@ -0,0 +1,71 @@ +// *************************************************************************************************************************** +// * 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 org.apache.juneau.internal; + +/** + * System utilities. + */ +public class SystemUtils { + + /** + * Returns the first non-<jk>null</jk> system property. + * + * @param def + * The default value if none are found. + * Can be <jk>null</jk>. + * @param keys + * The system properties to look for. + * @return + * The first non-<jk>null</jk> system property, or the default value if non were found. + */ + public static String getFirstString(String def, String...keys) { + for (String key : keys) { + String v = System.getProperty(key); + if (v != null) + return v; + } + return def; + } + + /** + * Returns the first non-<jk>null</jk> boolean system property. + * + * @param def + * The default value if none are found. + * Can be <jk>null</jk>. + * @param keys + * The system properties to look for. + * @return + * The first non-<jk>null</jk> system property, or the default value if non were found. + */ + public static Boolean getFirstBoolean(Boolean def, String...keys) { + String s = getFirstString(null, keys); + return s == null ? def : Boolean.parseBoolean(s); + } + + /** + * Returns the first non-<jk>null</jk> integer system property. + * + * @param def + * The default value if none are found. + * Can be <jk>null</jk>. + * @param keys + * The system properties to look for. + * @return + * The first non-<jk>null</jk> system property, or the default value if non were found. + */ + public static Integer getFirstInteger(Integer def, String...keys) { + String s = getFirstString(null, keys); + return s == null ? def : Integer.parseInt(s); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlClassMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlClassMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlClassMeta.java new file mode 100644 index 0000000..b1b22bb --- /dev/null +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlClassMeta.java @@ -0,0 +1,45 @@ +// *************************************************************************************************************************** +// * 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 org.apache.juneau.yaml.proto; + +import org.apache.juneau.*; +import org.apache.juneau.internal.*; +import org.apache.juneau.json.annotation.*; + +/** + * Metadata on classes specific to the JSON serializers and parsers pulled from the {@link Json @Json} annotation on + * the class. + */ +public class YamlClassMeta extends ClassMetaExtended { + + private final Json json; + + /** + * Constructor. + * + * @param cm The class that this annotation is defined on. + */ + public YamlClassMeta(ClassMeta<?> cm) { + super(cm); + this.json = ReflectionUtils.getAnnotation(Json.class, getInnerClass()); + } + + /** + * Returns the {@link Json} annotation defined on the class. + * + * @return The value of the {@link Json} annotation, or <jk>null</jk> if not specified. + */ + protected Json getAnnotation() { + return json; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParser.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParser.java new file mode 100644 index 0000000..5ddcf33 --- /dev/null +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParser.java @@ -0,0 +1,163 @@ +// *************************************************************************************************************************** +// * 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 org.apache.juneau.yaml.proto; + +import static org.apache.juneau.parser.ParserContext.*; + +import org.apache.juneau.*; +import org.apache.juneau.parser.*; + +/** + * Parses any valid JSON text into a POJO model. + * + * <h5 class='section'>Media types:</h5> + * + * Handles <code>Content-Type</code> types: <code>application/json, text/json</code> + * + * <h5 class='section'>Description:</h5> + * + * This parser uses a state machine, which makes it very fast and efficient. It parses JSON in about 70% of the + * time that it takes the built-in Java DOM parsers to parse equivalent XML. + * + * <p> + * This parser handles all valid JSON syntax. + * In addition, when strict mode is disable, the parser also handles the following: + * <ul class='spaced-list'> + * <li> + * Javascript comments (both {@code /*} and {@code //}) are ignored. + * <li> + * Both single and double quoted strings. + * <li> + * Automatically joins concatenated strings (e.g. <code><js>"aaa"</js> + <js>'bbb'</js></code>). + * <li> + * Unquoted attributes. + * </ul> + * + * <p> + * Also handles negative, decimal, hexadecimal, octal, and double numbers, including exponential notation. + * + * <p> + * This parser handles the following input, and automatically returns the corresponding Java class. + * <ul class='spaced-list'> + * <li> + * JSON objects (<js>"{...}"</js>) are converted to {@link ObjectMap ObjectMaps}. + * <b>Note:</b> If a <code><xa>_type</xa>=<xs>'xxx'</xs></code> attribute is specified on the object, then an + * attempt is made to convert the object to an instance of the specified Java bean class. + * See the <code>beanTypeName</code> setting on the {@link PropertyStore} for more information about parsing + * beans from JSON. + * <li> + * JSON arrays (<js>"[...]"</js>) are converted to {@link ObjectList ObjectLists}. + * <li> + * JSON string literals (<js>"'xyz'"</js>) are converted to {@link String Strings}. + * <li> + * JSON numbers (<js>"123"</js>, including octal/hexadecimal/exponential notation) are converted to + * {@link Integer Integers}, {@link Long Longs}, {@link Float Floats}, or {@link Double Doubles} depending on + * whether the number is decimal, and the size of the number. + * <li> + * JSON booleans (<js>"false"</js>) are converted to {@link Boolean Booleans}. + * <li> + * JSON nulls (<js>"null"</js>) are converted to <jk>null</jk>. + * <li> + * Input consisting of only whitespace or JSON comments are converted to <jk>null</jk>. + * </ul> + * + * <p> + * Input can be any of the following: + * <ul class='spaced-list'> + * <li> + * <js>"{...}"</js> - Converted to a {@link ObjectMap} or an instance of a Java bean if a <xa>_type</xa> + * attribute is present. + * <li> + * <js>"[...]"</js> - Converted to a {@link ObjectList}. + * <li> + * <js>"123..."</js> - Converted to a {@link Number} (either {@link Integer}, {@link Long}, {@link Float}, + * or {@link Double}). + * <li> + * <js>"true"</js>/<js>"false"</js> - Converted to a {@link Boolean}. + * <li> + * <js>"null"</js> - Returns <jk>null</jk>. + * <li> + * <js>"'xxx'"</js> - Converted to a {@link String}. + * <li> + * <js>"\"xxx\""</js> - Converted to a {@link String}. + * <li> + * <js>"'xxx' + \"yyy\""</js> - Converted to a concatenated {@link String}. + * </ul> + * + * <p> + * TIP: If you know you're parsing a JSON object or array, it can be easier to parse it using the + * {@link ObjectMap#ObjectMap(CharSequence) ObjectMap(CharSequence)} or {@link ObjectList#ObjectList(CharSequence) + * ObjectList(CharSequence)} constructors instead of using this class. + * The end result should be the same. + * + * <h5 class='section'>Configurable properties:</h5> + * + * This class has the following properties associated with it: + * <ul> + * <li>{@link YamlParserContext} + * </ul> + */ +public class YamlParser extends ReaderParser { + + /** Default parser, all default settings.*/ + public static final YamlParser DEFAULT = new YamlParser(PropertyStore.create()); + + /** Default parser, all default settings.*/ + public static final YamlParser DEFAULT_STRICT = new YamlParser.Strict(PropertyStore.create()); + + /** Default parser, strict mode. */ + public static class Strict extends YamlParser { + + /** + * Constructor. + * + * @param propertyStore The property store containing all the settings for this object. + */ + public Strict(PropertyStore propertyStore) { + super(propertyStore.copy().append(PARSER_strict, true)); + } + } + + + private final YamlParserContext ctx; + + /** + * Constructor. + * + * @param propertyStore The property store containing all the settings for this object. + */ + public YamlParser(PropertyStore propertyStore) { + this(propertyStore, "application/json", "text/json"); + } + + /** + * Constructor. + * + * @param propertyStore The property store containing all the settings for this object. + * @param consumes The list of media types that this parser consumes (e.g. <js>"application/json"</js>). + */ + public YamlParser(PropertyStore propertyStore, String...consumes) { + super(propertyStore, consumes); + this.ctx = createContext(YamlParserContext.class); + } + + @Override /* CoreObject */ + public YamlParserBuilder builder() { + return new YamlParserBuilder(propertyStore); + } + + @Override /* Parser */ + public ReaderParserSession createSession(ParserSessionArgs args) { + return new YamlParserSession(ctx, args); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserBuilder.java new file mode 100644 index 0000000..c1c77d2 --- /dev/null +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserBuilder.java @@ -0,0 +1,465 @@ +// *************************************************************************************************************************** +// * 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 org.apache.juneau.yaml.proto; + +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.http.*; +import org.apache.juneau.parser.*; + +/** + * Builder class for building instances of JSON parsers. + */ +public class YamlParserBuilder extends ParserBuilder { + + /** + * Constructor, default settings. + */ + public YamlParserBuilder() { + super(); + } + + /** + * Constructor. + * + * @param propertyStore The initial configuration settings for this builder. + */ + public YamlParserBuilder(PropertyStore propertyStore) { + super(propertyStore); + } + + @Override /* CoreObjectBuilder */ + public YamlParser build() { + return new YamlParser(propertyStore); + } + + + //-------------------------------------------------------------------------------- + // Properties + //-------------------------------------------------------------------------------- + + @Override /* ParserBuilder */ + public YamlParserBuilder trimStrings(boolean value) { + super.trimStrings(value); + return this; + } + + @Override /* ParserBuilder */ + public YamlParserBuilder strict(boolean value) { + super.strict(value); + return this; + } + + @Override /* ParserBuilder */ + public YamlParserBuilder strict() { + super.strict(); + return this; + } + + @Override /* ParserBuilder */ + public YamlParserBuilder inputStreamCharset(String value) { + super.inputStreamCharset(value); + return this; + } + + @Override /* ParserBuilder */ + public YamlParserBuilder fileCharset(String value) { + super.fileCharset(value); + return this; + } + + @Override /* ParserBuilder */ + public YamlParserBuilder listener(Class<? extends ParserListener> value) { + super.listener(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beansRequireDefaultConstructor(boolean value) { + super.beansRequireDefaultConstructor(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beansRequireSerializable(boolean value) { + super.beansRequireSerializable(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beansRequireSettersForGetters(boolean value) { + super.beansRequireSettersForGetters(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beansRequireSomeProperties(boolean value) { + super.beansRequireSomeProperties(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beanMapPutReturnsOldValue(boolean value) { + super.beanMapPutReturnsOldValue(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beanConstructorVisibility(Visibility value) { + super.beanConstructorVisibility(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beanClassVisibility(Visibility value) { + super.beanClassVisibility(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beanFieldVisibility(Visibility value) { + super.beanFieldVisibility(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder methodVisibility(Visibility value) { + super.methodVisibility(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder useJavaBeanIntrospector(boolean value) { + super.useJavaBeanIntrospector(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder useInterfaceProxies(boolean value) { + super.useInterfaceProxies(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder ignoreUnknownBeanProperties(boolean value) { + super.ignoreUnknownBeanProperties(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder ignoreUnknownNullBeanProperties(boolean value) { + super.ignoreUnknownNullBeanProperties(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder ignorePropertiesWithoutSetters(boolean value) { + super.ignorePropertiesWithoutSetters(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder ignoreInvocationExceptionsOnGetters(boolean value) { + super.ignoreInvocationExceptionsOnGetters(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder ignoreInvocationExceptionsOnSetters(boolean value) { + super.ignoreInvocationExceptionsOnSetters(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder sortProperties(boolean value) { + super.sortProperties(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder notBeanPackages(String...values) { + super.notBeanPackages(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder notBeanPackages(Collection<String> values) { + super.notBeanPackages(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setNotBeanPackages(String...values) { + super.setNotBeanPackages(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setNotBeanPackages(Collection<String> values) { + super.setNotBeanPackages(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removeNotBeanPackages(String...values) { + super.removeNotBeanPackages(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removeNotBeanPackages(Collection<String> values) { + super.removeNotBeanPackages(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder notBeanClasses(Class<?>...values) { + super.notBeanClasses(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder notBeanClasses(Collection<Class<?>> values) { + super.notBeanClasses(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setNotBeanClasses(Class<?>...values) { + super.setNotBeanClasses(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setNotBeanClasses(Collection<Class<?>> values) { + super.setNotBeanClasses(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removeNotBeanClasses(Class<?>...values) { + super.removeNotBeanClasses(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removeNotBeanClasses(Collection<Class<?>> values) { + super.removeNotBeanClasses(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beanFilters(Class<?>...values) { + super.beanFilters(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beanFilters(Collection<Class<?>> values) { + super.beanFilters(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setBeanFilters(Class<?>...values) { + super.setBeanFilters(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setBeanFilters(Collection<Class<?>> values) { + super.setBeanFilters(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removeBeanFilters(Class<?>...values) { + super.removeBeanFilters(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removeBeanFilters(Collection<Class<?>> values) { + super.removeBeanFilters(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder pojoSwaps(Class<?>...values) { + super.pojoSwaps(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder pojoSwaps(Collection<Class<?>> values) { + super.pojoSwaps(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setPojoSwaps(Class<?>...values) { + super.setPojoSwaps(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setPojoSwaps(Collection<Class<?>> values) { + super.setPojoSwaps(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removePojoSwaps(Class<?>...values) { + super.removePojoSwaps(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removePojoSwaps(Collection<Class<?>> values) { + super.removePojoSwaps(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder implClasses(Map<Class<?>,Class<?>> values) { + super.implClasses(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public <T> YamlParserBuilder implClass(Class<T> interfaceClass, Class<? extends T> implClass) { + super.implClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beanDictionary(Class<?>...values) { + super.beanDictionary(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beanDictionary(Collection<Class<?>> values) { + super.beanDictionary(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setBeanDictionary(Class<?>...values) { + super.setBeanDictionary(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder setBeanDictionary(Collection<Class<?>> values) { + super.setBeanDictionary(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removeFromBeanDictionary(Class<?>...values) { + super.removeFromBeanDictionary(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removeFromBeanDictionary(Collection<Class<?>> values) { + super.removeFromBeanDictionary(values); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder beanTypePropertyName(String value) { + super.beanTypePropertyName(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder defaultParser(Class<?> value) { + super.defaultParser(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder locale(Locale value) { + super.locale(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder timeZone(TimeZone value) { + super.timeZone(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder mediaType(MediaType value) { + super.mediaType(value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder debug() { + super.debug(); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder property(String name, Object value) { + super.property(name, value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder properties(Map<String,Object> properties) { + super.properties(properties); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder addToProperty(String name, Object value) { + super.addToProperty(name, value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder putToProperty(String name, Object key, Object value) { + super.putToProperty(name, key, value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder putToProperty(String name, Object value) { + super.putToProperty(name, value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder removeFromProperty(String name, Object value) { + super.removeFromProperty(name, value); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder classLoader(ClassLoader classLoader) { + super.classLoader(classLoader); + return this; + } + + @Override /* CoreObjectBuilder */ + public YamlParserBuilder apply(PropertyStore copyFrom) { + super.apply(copyFrom); + return this; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ab8f0faa/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserContext.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserContext.java new file mode 100644 index 0000000..48b589d --- /dev/null +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/yaml/proto/YamlParserContext.java @@ -0,0 +1,61 @@ +// *************************************************************************************************************************** +// * 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 org.apache.juneau.yaml.proto; + +import org.apache.juneau.*; +import org.apache.juneau.parser.*; + +/** + * Configurable properties on the {@link YamlParser} class. + * + * <p> + * Context properties are set by calling {@link PropertyStore#setProperty(String, Object)} on the property store + * passed into the constructor. + * + * <p> + * See {@link PropertyStore} for more information about context properties. + * + * <h6 class='topic'>Inherited configurable properties</h6> + * <ul class='doctree'> + * <li class='jc'> + * <a class="doclink" href="../BeanContext.html#ConfigProperties">BeanContext</a> + * - Properties associated with handling beans on serializers and parsers. + * <ul> + * <li class='jc'> + * <a class="doclink" href="../parser/ParserContext.html#ConfigProperties">ParserContext</a> + * - Configurable properties common to all parsers. + * </ul> + * </li> + * </ul> + */ +public final class YamlParserContext extends ParserContext { + + /** + * Constructor. + * + * <p> + * Typically only called from {@link PropertyStore#getContext(Class)}. + * + * @param ps The property store that created this context. + */ + public YamlParserContext(PropertyStore ps) { + super(ps); + } + + @Override /* Context */ + public ObjectMap asMap() { + return super.asMap() + .append("JsonParserContext", new ObjectMap() + ); + } +}