You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2019/03/10 15:52:30 UTC
[juneau] 05/09: Add RestResponse access to HTML widgets.
This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
commit f6e2dc415d8ef486d451e5007b8214cd0637a891
Author: JamesBognar <ja...@apache.org>
AuthorDate: Mon Feb 18 10:39:20 2019 -0500
Add RestResponse access to HTML widgets.
---
.../java/org/apache/juneau/config/ConfigTest.java | 2 +-
.../apache/juneau/pojotools/PojoSearcherTest.java | 1056 ++++++++++++++++++++
.../apache/juneau/pojotools/PojoSorterTest.java | 265 +++++
.../apache/juneau/pojotools/PojoViewerTest.java | 349 +++++++
.../apache/juneau/internal/DelegateBeanMap.java | 8 +-
.../org/apache/juneau/internal/DelegateMap.java | 19 +-
.../org/apache/juneau/internal/ObjectUtils.java | 28 +
.../apache/juneau/internal/StateMachineState.java | 31 +-
.../org/apache/juneau/internal/StringUtils.java | 76 +-
.../java/org/apache/juneau/pojotools/Equality.java | 33 +-
.../java/org/apache/juneau/pojotools/Matcher.java | 30 +-
.../apache/juneau/pojotools/MatcherFactory.java | 35 +-
.../juneau/pojotools/NumberMatcherFactory.java | 291 ++++++
.../apache/juneau/pojotools/PatternException.java | 29 +-
.../org/apache/juneau/pojotools/PojoPaginator.java | 71 ++
.../org/apache/juneau/pojotools/PojoSearcher.java | 185 ++++
.../org/apache/juneau/pojotools/PojoSorter.java | 110 ++
.../java/org/apache/juneau/pojotools/PojoTool.java | 31 +-
.../org/apache/juneau/pojotools/PojoViewer.java | 80 ++
.../{utils/ASet.java => pojotools/SearchArgs.java} | 73 +-
.../DelegateMap.java => pojotools/SortArgs.java} | 138 +--
.../juneau/pojotools/StringMatcherFactory.java | 145 +++
.../juneau/pojotools/TimeMatcherFactory.java | 463 +++++++++
.../java/org/apache/juneau/pojotools/ViewArgs.java | 59 +-
.../main/java/org/apache/juneau/utils/ASet.java | 1 +
.../java/org/apache/juneau/utils/PojoQuery.java | 2 +-
juneau-doc/docs/ReleaseNotes/8.0.1.html | 2 +
.../rest/petstore/rest/AddOrderMenuItem.java | 6 +-
.../rest/petstore/rest/AddPetMenuItem.java | 4 +-
.../rest/petstore/rest/UploadPhotoMenuItem.java | 4 +-
.../apache/juneau/rest/BasicRestCallHandler.java | 3 +-
.../org/apache/juneau/rest/RestCallHandler.java | 3 +-
.../java/org/apache/juneau/rest/RestContext.java | 6 +-
.../java/org/apache/juneau/rest/RestRequest.java | 3 +-
.../org/apache/juneau/rest/annotation/HtmlDoc.java | 6 +-
.../org/apache/juneau/rest/vars/RequestVar.java | 5 +
.../org/apache/juneau/rest/vars/WidgetVar.java | 8 +-
.../juneau/rest/widget/ContentTypeMenuItem.java | 4 +-
.../apache/juneau/rest/widget/MenuItemWidget.java | 32 +-
.../apache/juneau/rest/widget/PoweredByApache.java | 2 +-
.../apache/juneau/rest/widget/PoweredByJuneau.java | 2 +-
.../apache/juneau/rest/widget/QueryMenuItem.java | 8 +-
.../apache/juneau/rest/widget/ThemeMenuItem.java | 4 +-
.../java/org/apache/juneau/rest/widget/Widget.java | 18 +-
44 files changed, 3414 insertions(+), 316 deletions(-)
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigTest.java
index 9555f64..a0d0ed8 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/config/ConfigTest.java
@@ -1742,7 +1742,7 @@ public class ConfigTest {
assertObjectEquals("['foo.txt']", Config.getCandidateSystemDefaultConfigNames());
System.clearProperty("juneau.configFile");
- assertObjectEquals("['test.cfg','juneau.cfg','default.cfg']", Config.getCandidateSystemDefaultConfigNames());
+ assertObjectEquals("['test.cfg','juneau.cfg','default.cfg','application.cfg','app.cfg','settings.cfg']", Config.getCandidateSystemDefaultConfigNames());
}
}
\ No newline at end of file
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/pojotools/PojoSearcherTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/pojotools/PojoSearcherTest.java
new file mode 100755
index 0000000..8374d58
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/pojotools/PojoSearcherTest.java
@@ -0,0 +1,1056 @@
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import static org.apache.juneau.testutils.TestUtils.*;
+import static org.junit.Assert.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transforms.*;
+import org.apache.juneau.utils.*;
+import org.junit.*;
+
+/**
+ * Tests the PojoSearcher class.
+ */
+public class PojoSearcherTest {
+
+ private static BeanSession bs = BeanContext.DEFAULT.createSession();
+ private static PojoSearcher ps = PojoSearcher.DEFAULT;
+ private static WriterSerializer ws = JsonSerializer.create().ssq().pojoSwaps(CalendarSwap.DateTimeSimple.class).build();
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Utility
+ //-----------------------------------------------------------------------------------------------------------------
+
+ static SearchArgs[] create(String...search) {
+ SearchArgs[] sa = new SearchArgs[search.length];
+ for (int i = 0; i < search.length; i++)
+ sa[i] = new SearchArgs(search[i]);
+ return sa;
+ }
+
+ static SearchArgs create(String search) {
+ return new SearchArgs(search);
+ }
+
+ static Object run(Object in, String search) {
+ return ps.run(bs, in, create(search));
+ }
+
+ static Object run(Object in, SearchArgs sa) {
+ return ps.run(bs, in, sa);
+ }
+
+ static String[] a(String...s) {
+ return s;
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // String search
+ //-----------------------------------------------------------------------------------------------------------------
+
+ public static class A {
+ public String f;
+
+ public static A create(String f) {
+ A a = new A();
+ a.f = f;
+ return a;
+ }
+ }
+
+ public static List<A> A_LIST = AList.create(A.create("foo"), A.create("bar"), A.create("baz"), A.create("q ux"), A.create("qu'ux"), null, A.create(null));
+ public static Set<A> A_SET = ASet.create(A.create("foo"), A.create("bar"), A.create("baz"), A.create("q ux"), A.create("qu'ux"), null, A.create(null));
+ public static A[] A_ARRAY = new A[]{A.create("foo"), A.create("bar"), A.create("baz"), A.create("q ux"), A.create("qu'ux"), null, A.create(null)};
+
+ @Test
+ public void stringSearch_singleWord() throws Exception {
+ assertObjectEquals("[{f:'foo'}]", run(A_LIST, "f=foo"));
+ assertObjectEquals("[{f:'foo'}]", run(A_SET, "f=foo"));
+ assertObjectEquals("[{f:'foo'}]", run(A_ARRAY, "f=foo"));
+ }
+
+ @Test
+ public void stringSearch_pattern1() throws Exception {
+ assertObjectEquals("[{f:'foo'}]", run(A_LIST, "f=fo*"));
+ assertObjectEquals("[{f:'foo'}]", run(A_SET, "f=fo*"));
+ assertObjectEquals("[{f:'foo'}]", run(A_ARRAY, "f=fo*"));
+ }
+
+ @Test
+ public void stringSearch_pattern2() throws Exception {
+ assertObjectEquals("[{f:'bar'}]", run(A_LIST, "f=*ar"));
+ assertObjectEquals("[{f:'bar'}]", run(A_SET, "f=*ar"));
+ assertObjectEquals("[{f:'bar'}]", run(A_ARRAY, "f=*ar"));
+ }
+
+ @Test
+ public void stringSearch_pattern3() throws Exception {
+ assertObjectEquals("[{f:'bar'}]", run(A_LIST, "f=?ar"));
+ assertObjectEquals("[{f:'bar'}]", run(A_SET, "f=?ar"));
+ assertObjectEquals("[{f:'bar'}]", run(A_ARRAY, "f=?ar"));
+ }
+
+ @Test
+ public void stringSearch_multiple() throws Exception {
+ assertObjectEquals("[{f:'foo'},{f:'bar'}]", run(A_LIST, "f=foo bar q ux"));
+ assertObjectEquals("[{f:'foo'},{f:'bar'}]", run(A_SET, "f=foo bar q ux"));
+ assertObjectEquals("[{f:'foo'},{f:'bar'}]", run(A_ARRAY, "f=foo bar q ux"));
+ }
+
+ @Test
+ public void stringSearch_quoted() throws Exception {
+ assertObjectEquals("[{f:'q ux'}]", run(A_LIST, "f='q ux'"));
+ assertObjectEquals("[{f:'q ux'}]", run(A_SET, "f='q ux'"));
+ assertObjectEquals("[{f:'q ux'}]", run(A_ARRAY, "f='q ux'"));
+ }
+
+ @Test
+ public void stringSearch_quotedWithPattern() throws Exception {
+ assertObjectEquals("[{f:'q ux'}]", run(A_LIST, "f='q *x'"));
+ assertObjectEquals("[{f:'q ux'}]", run(A_SET, "f='q *x'"));
+ assertObjectEquals("[{f:'q ux'}]", run(A_ARRAY, "f='q *x'"));
+ }
+
+ @Test
+ public void stringSearch_unquotedContainingQuote() throws Exception {
+ assertObjectEquals("[{f:'qu\\'ux'}]", run(A_LIST, "f=qu'ux"));
+ assertObjectEquals("[{f:'qu\\'ux'}]", run(A_SET, "f=qu'ux"));
+ assertObjectEquals("[{f:'qu\\'ux'}]", run(A_ARRAY, "f=qu'ux"));
+ }
+
+ @Test
+ public void stringSearch_quotedContainingQuote() throws Exception {
+ assertObjectEquals("[{f:'qu\\'ux'}]", run(A_LIST, "f='qu\\'ux'"));
+ assertObjectEquals("[{f:'qu\\'ux'}]", run(A_SET, "f='qu\\'ux'"));
+ assertObjectEquals("[{f:'qu\\'ux'}]", run(A_ARRAY, "f='qu\\'ux'"));
+ }
+
+ @Test
+ public void stringSearch_regExp() throws Exception {
+ assertObjectEquals("[{f:'q ux'}]", run(A_LIST, "f=/q\\sux/"));
+ assertObjectEquals("[{f:'q ux'}]", run(A_SET, "f=/q\\sux/"));
+ assertObjectEquals("[{f:'q ux'}]", run(A_ARRAY, "f=/q\\sux/"));
+ }
+
+ @Test
+ public void stringSearch_regExp_noEndSlash() throws Exception {
+ Object in = AList.create(A.create("/foo"), A.create("bar"));
+ for (String s : a("f=/foo","f='/foo'")) // Not a regex.
+ assertObjectEquals("[{f:'/foo'}]", run(in, s));
+ }
+
+ @Test
+ public void stringSearch_regExp_onlySlash() throws Exception {
+ Object in = AList.create(A.create("/"), A.create("bar"));
+ for (String s : a("f=/", "f='/'")) // Not a regex.
+ assertObjectEquals("[{f:'/'}]", run(in, s));
+ }
+
+ @Test
+ public void stringSearch_or_pattern() throws Exception {
+ Object in = AList.create(A.create("foo"), A.create("bar"), A.create("baz"));
+ assertObjectEquals("[{f:'foo'},{f:'bar'}]", run(in, "f=f* *r"));
+ assertObjectEquals("[]", run(in, "f='f* *r'"));
+ assertObjectEquals("[{f:'foo'}]", run(in, "f='f*oo'"));
+ }
+
+ @Test
+ public void stringSearch_explicit_or_pattern() throws Exception {
+ Object in = AList.create(A.create("foo"), A.create("bar"), A.create("baz"));
+ assertObjectEquals("[{f:'foo'},{f:'bar'}]", run(in, "f=^f* ^*r"));
+ assertObjectEquals("[]", run(in, "f=^'f* *r'"));
+ assertObjectEquals("[{f:'foo'}]", run(in, "f=^'f*oo'"));
+ }
+
+ @Test
+ public void stringSearch_and_pattern() throws Exception {
+ Object in = AList.create(A.create("foo"), A.create("bar"), A.create("baz"));
+ assertObjectEquals("[{f:'bar'}]", run(in, "f=+b* +*r"));
+ assertObjectEquals("[{f:'bar'}]", run(in, "f=+'b*' +'*r'"));
+ }
+
+ @Test
+ public void stringSearch_not_pattern() throws Exception {
+ Object in = AList.create(A.create("foo"), A.create("bar"), A.create("baz"));
+ assertObjectEquals("[{f:'baz'}]", run(in, "f=b* -*r"));
+ assertObjectEquals("[{f:'baz'}]", run(in, "f=+'b*' -'*r'"));
+ }
+
+ @Test
+ public void stringSearch_caseSensitive() throws Exception {
+ Object in = AList.create(A.create("foo"), A.create("bar"), A.create("baz"));
+ assertObjectEquals("[]", run(in, "f=F*"));
+ assertObjectEquals("[]", run(in, "f=\"F*\""));
+ assertObjectEquals("[{f:'foo'}]", run(in, "f='F*'"));
+ }
+
+ @Test
+ public void stringSearch_malformedQuotes() throws Exception {
+ Object in = AList.create(A.create("'foo"), A.create("\"bar"), A.create("baz"));
+
+ try {
+ run(in, "f='*");
+ fail();
+ } catch (Exception e) {
+ assertTrue(e.getLocalizedMessage().contains("Unmatched string quotes"));
+ }
+
+ try {
+ run(in, "f=\"*");
+ fail();
+ } catch (Exception e) {
+ assertTrue(e.getLocalizedMessage().contains("Unmatched string quotes"));
+ }
+
+ assertObjectEquals("[{f:'\\'foo'}]", run(in, "f='\\'*'"));
+ assertObjectEquals("[{f:'\"bar'}]", run(in, "f='\"*'"));
+ assertObjectEquals("[{f:'\"bar'}]", run(in, "f=\"\\\"*\""));
+ }
+
+ @Test
+ public void stringSearch_regexChars() throws Exception {
+ Object in = AList.create(A.create("+\\[]{}()^$."), A.create("bar"), A.create("baz"));
+ assertObjectEquals("[{f:'+\\\\[]{}()^$.'}]", run(in, "f=*+*"));
+ assertObjectEquals("[{f:'+\\\\[]{}()^$.'}]", run(in, "f='+\\\\[]{}()^$.'"));
+ assertObjectEquals("[{f:'+\\\\[]{}()^$.'}]", run(in, "f=++\\\\[]{}()^$."));
+ }
+
+ @Test
+ public void stringSearch_metaChars() throws Exception {
+ Object in = AList.create(A.create("*?\\'\""), A.create("bar"), A.create("baz"));
+ assertObjectEquals("[{f:'*?\\\\\\'\"'}]", run(in, "f='\\*\\?\\\\\\'\"'"));
+ }
+
+ @Test
+ public void stringSearch_metaChars_escapedQuotes() throws Exception {
+ Object in = AList.create(A.create("'"), A.create("\""), A.create("baz"));
+ assertObjectEquals("[{f:'\\''}]", run(in, "f=\\'"));
+ assertObjectEquals("[{f:'\"'}]", run(in, "f=\\\""));
+ }
+
+ @Test
+ public void stringSearch_metaChars_falseEscape() throws Exception {
+ Object in = AList.create(A.create("foo"), A.create("bar"), A.create("baz"));
+ assertObjectEquals("[{f:'foo'}]", run(in, "f=\\f\\o\\o"));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Number search
+ //-----------------------------------------------------------------------------------------------------------------
+
+ public static class C {
+ public int f;
+
+ static C create(int f) {
+ C c = new C();
+ c.f = f;
+ return c;
+ }
+ }
+
+ C[] INT_BEAN_ARRAY = new C[]{C.create(-2), C.create(-1), C.create(0), C.create(1), C.create(2), C.create(3)};
+
+ @Test
+ public void intSearch_oneNumber() throws Exception {
+ for (String s : a("f=1", "f = 1"))
+ assertObjectEquals("[{f:1}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_twoNumbers() throws Exception {
+ for (String s : a("f=1 2", "f = 1 2 "))
+ assertObjectEquals("[{f:1},{f:2}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_oneNegativeNumber() throws Exception {
+ for (String s : a("f=-1", "f = -1 "))
+ assertObjectEquals("[{f:-1}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_twoNegativeNumbers() throws Exception {
+ assertObjectEquals("[{f:-2},{f:-1}]", run(INT_BEAN_ARRAY, "f=-1 -2"));
+ }
+
+ @Test
+ public void intSearch_simpleRange() throws Exception {
+ for (String s : a("f=1-2", "f = 1 - 2 ", "f = 1- 2 "))
+ assertObjectEquals("[{f:1},{f:2}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_simpleRange_invalid() throws Exception {
+ assertObjectEquals("[]", run(INT_BEAN_ARRAY, "f=2-1"));
+ }
+
+ @Test
+ public void intSearch_twoNumbersThatLookLikeRange() throws Exception {
+ assertObjectEquals("[{f:-2},{f:1}]", run(INT_BEAN_ARRAY, "f = 1 -2 "));
+ }
+
+ @Test
+ public void intSearch_rangeWithNegativeNumbers() throws Exception {
+ assertObjectEquals("[{f:-2},{f:-1}]", run(INT_BEAN_ARRAY, "f = -2--1 "));
+ }
+
+ @Test
+ public void intSearch_rangeWithNegativeNumbers_invalidRange() throws Exception {
+ assertObjectEquals("[]", run(INT_BEAN_ARRAY, "f = -1--2 "));
+ }
+
+ @Test
+ public void intSearch_multipleRanges() throws Exception {
+ assertObjectEquals("[{f:0},{f:1},{f:3}]", run(INT_BEAN_ARRAY, "f = 0-1 3-4"));
+ }
+
+ @Test
+ public void intSearch_overlappingRanges() throws Exception {
+ assertObjectEquals("[{f:0},{f:2}]", run(INT_BEAN_ARRAY, "f = 0-0 2-2"));
+ }
+
+ @Test
+ public void intSearch_LT() throws Exception {
+ for (String s : a("f = <0", "f<0", "f = < 0 ", "f < 0 "))
+ assertObjectEquals("[{f:-2},{f:-1}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_LT_negativeNumber() throws Exception {
+ for (String s : a("f = <-1", "f<-1", "f = < -1 ", "f < -1 "))
+ assertObjectEquals("[{f:-2}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_GT() throws Exception {
+ for (String s : a("f = >1", "f>1", "f = > 1 ", "f > 1 "))
+ assertObjectEquals("[{f:2},{f:3}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_GT_negativeNumber() throws Exception {
+ for (String s : a("f = >-1", "f>-1", "f = > -1 ", "f > -1 ", "f = > -1 ", "f > -1 "))
+ assertObjectEquals("[{f:0},{f:1},{f:2},{f:3}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_LTE() throws Exception {
+ for (String s : a("f = <=0", "f<=0", "f = <= 0 ", "f <= 0 ", "f = <= 0 "))
+ assertObjectEquals("[{f:-2},{f:-1},{f:0}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_LTE_negativeNumber() throws Exception {
+ for (String s : a("f = <=-1", "f <=-1", "f = <= -1 ", "f = <= -1 ", "f <= -1 "))
+ assertObjectEquals("[{f:-2},{f:-1}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_GTE() throws Exception {
+ for (String s : a("f = >=1", "f >=1", "f = >= 1 ", "f >= 1 ", "f = >= 1 "))
+ assertObjectEquals("[{f:1},{f:2},{f:3}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_GTE_negativeNumber() throws Exception {
+ for (String s : a("f = >=-1", "f >=-1", "f = >= -1 ", "f >= -1 ", "f = >= -1 "))
+ assertObjectEquals("[{f:-1},{f:0},{f:1},{f:2},{f:3}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_not_singleNumber() throws Exception {
+ for (String s : a("f = !1", "f = ! 1 ", "f = ! 1 "))
+ assertObjectEquals("[{f:-2},{f:-1},{f:0},{f:2},{f:3}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_not_range() throws Exception {
+ assertObjectEquals("[{f:-2},{f:-1},{f:0},{f:3}]", run(INT_BEAN_ARRAY, "f = !1-2"));
+ }
+
+ @Test
+ public void intSearch_not_range_negativeNumbers() throws Exception {
+ for (String s : a("f = !-2--1", "f = ! -2 - -1", "f = ! -2 - -1 "))
+ assertObjectEquals("[{f:0},{f:1},{f:2},{f:3}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_not_looksLikeRange() throws Exception {
+ assertObjectEquals("[{f:-2},{f:-1},{f:0},{f:1},{f:2},{f:3}]", run(INT_BEAN_ARRAY, "f = ! -2 -2"));
+ }
+
+ @Test
+ public void intSearch_empty() throws Exception {
+ for (String s : a("f=", "f = ", "f = "))
+ assertObjectEquals("[{f:-2},{f:-1},{f:0},{f:1},{f:2},{f:3}]", run(INT_BEAN_ARRAY, s));
+ }
+
+ @Test
+ public void intSearch_badSearches() throws Exception {
+ String[] ss = new String[] {
+ "f=x","(S01)",
+ "f=>x","(S02)",
+ "f=<x","(S03)",
+ "f=>=x","(S04)",
+ "f=>= x","(S05)",
+ "f=1x","(S06)",
+ "f=1 x","(S07)",
+ "f=1-x","(S08)",
+ "f=1 -x","(S09)",
+ "f=1 - x","(S10)",
+ "f=1 - 1x","(S11)",
+ "f=>","(ES02)",
+ "f=<","(ES03)",
+ "f=>=","(ES04)",
+ "f=123-","(ES08)",
+ "f=123 -","(ES09)",
+ };
+
+ for (int i = 0; i < ss.length; i+=2) {
+ try {
+ run(INT_BEAN_ARRAY, ss[i]);
+ fail("i=" + i);
+ } catch (PatternException e) {
+ assertTrue(e.getLocalizedMessage().contains(ss[i+1]));
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Date search
+ //-----------------------------------------------------------------------------------------------------------------
+
+ public static class B {
+ public Calendar f;
+
+ static B[] create(String...dates) {
+ B[] bb = new B[dates.length];
+ for (int i = 0; i < dates.length; i++) {
+ bb[i] = new B();
+ bb[i].f = DateUtils.parseISO8601Calendar(dates[i]);
+ }
+ return bb;
+ }
+ }
+
+ @Test
+ public void dateSearch_singleDate_y() throws Exception {
+ B[] in = B.create("2010-01-01", "2011-01-01", "2011-01-31", "2012-01-01");
+ for (String s : a(
+ "f=2011",
+ "f = 2011 ",
+ "f = '2011' ",
+ "f = \"2011\" "
+ ))
+ assertObjectEquals("[{f:'2011/01/01 00:00:00'},{f:'2011/01/31 00:00:00'}]", run(in, s), ws);
+ }
+
+ @Test
+ public void dateSearch_singleDate_ym() throws Exception {
+ B[] in = B.create("2010-01-01", "2011-01-01", "2011-01-31", "2012-01-01");
+ for (String s : a(
+ "f=2011-01",
+ "f = 2011-01 ",
+ "f='2011-01'",
+ "f=\"2011-01\""
+ ))
+ assertObjectEquals("[{f:'2011/01/01 00:00:00'},{f:'2011/01/31 00:00:00'}]", run(in, s), ws);
+ }
+
+ @Test
+ public void dateSearch_singleDate_ymd() throws Exception {
+ B[] in = B.create("2010-01-01", "2011-01-01", "2011-01-31", "2012-01-01");
+ assertObjectEquals("[{f:'2011/01/01 00:00:00'}]", run(in, "f=2011-01-01"), ws);
+ }
+
+
+ @Test
+ public void dateSearch_singleDate_ymdh() throws Exception {
+ B[] in = B.create("2011-01-01T11:15:59", "2011-01-01T12:00:00", "2011-01-01T12:59:59", "2011-01-01T13:00:00");
+ assertObjectEquals("[{f:'2011/01/01 12:00:00'},{f:'2011/01/01 12:59:59'}]", run(in, "f=2011-01-01T12"), ws);
+ }
+
+ @Test
+ public void dateSearch_singleDate_ymdhm() throws Exception {
+ B[] in = B.create("2011-01-01T12:29:59", "2011-01-01T12:30:00", "2011-01-01T12:30:59", "2011-01-01T12:31:00");
+ assertObjectEquals("[{f:'2011/01/01 12:30:00'},{f:'2011/01/01 12:30:59'}]", run(in, "f=2011-01-01T12:30"), ws);
+ }
+
+ @Test
+ public void dateSearch_singleDate_ymdhms() throws Exception {
+ B[] in = B.create("2011-01-01T12:30:29", "2011-01-01T12:30:30", "2011-01-01T12:30:31");
+ assertObjectEquals("[{f:'2011/01/01 12:30:30'}]", run(in, "f=2011-01-01T12:30:30"), ws);
+ }
+
+ @Test
+ public void dateSearch_openEndedRanges_y() throws Exception {
+ B[] in = B.create("2000-12-31", "2001-01-01");
+ for (String s : a(
+ "f>2000",
+ "f > 2000 ",
+ "f>'2000'",
+ "f > '2000' ",
+ "f>\"2000\"",
+ "f > \"2000\" ",
+ "f>=2001",
+ "f >= 2001 ",
+ "f>='2001'",
+ "f >= '2001' ",
+ "f>=\"2001\"",
+ "f >= \"2001\" "
+ ))
+ assertObjectEquals("[{f:'2001/01/01 00:00:00'}]", run(in, s), ws);
+ for (String s : a(
+ "f<2001",
+ "f < 2001 ",
+ "f<'2001'",
+ "f < '2001'",
+ "f<\"2001\"",
+ "f < \"2001\" ",
+ "f<=2000",
+ "f <= 2000 ",
+ "f<='2000'",
+ "f <= '2000'",
+ "f<=\"2000\"",
+ "f <= \"2000\" "
+ ))
+ assertObjectEquals("[{f:'2000/12/31 00:00:00'}]", run(in, s), ws);
+ }
+
+ @Test
+ public void dateSearch_openEndedRanges_toMinute() throws Exception {
+ B[] in = B.create("2011-01-01T12:29:59", "2011-01-01T12:30:00");
+ assertObjectEquals("[{f:'2011/01/01 12:30:00'}]", run(in, "f>=2011-01-01T12:30"), ws);
+ assertObjectEquals("[{f:'2011/01/01 12:29:59'}]", run(in, "f<2011-01-01T12:30"), ws);
+ }
+
+ @Test
+ public void dateSearch_openEndedRanges_toSecond() throws Exception {
+ B[] in = B.create("2011-01-01T12:30:59", "2011-01-01T12:31:00");
+ assertObjectEquals("[{f:'2011/01/01 12:31:00'}]", run(in, "f>2011-01-01T12:30"), ws);
+ assertObjectEquals("[{f:'2011/01/01 12:30:59'}]", run(in, "f<=2011-01-01T12:30"), ws);
+ }
+
+ @Test
+ public void dateSearch_closedRanges() throws Exception {
+ B[] in = B.create("2000-12-31T23:59:59", "2001-01-01T00:00:00", "2003-06-30T23:59:59", "2003-07-01T00:00:00");
+
+ for (String s : a(
+ "f= 2001 - 2003-06-30 ",
+ "f= 2001 - 2003-06-30",
+ "f='2001'-'2003-06-30'",
+ "f= '2001' - '2003-06-30' ",
+ "f=\"2001\"-\"2003-06-30\"",
+ "f= \"2001\" - \"2003-06-30\" ",
+ "f=2001 -'2003-06-30'",
+ "f= 2001 - '2003-06-30' ",
+ "f=2001 -\"2003-06-30\"",
+ "f= 2001 - \"2003-06-30\" "
+ ))
+ assertObjectEquals("[{f:'2001/01/01 00:00:00'},{f:'2003/06/30 23:59:59'}]", run(in, s), ws);
+
+ for (String s : a(
+ "f= 2001 - 2003-06-30 2000",
+ "f= 2001 - 2003-06-30 '2000'",
+ "f= 2001 - 2003-06-30 \"2000\"",
+ "f='2001'-'2003-06-30' 2000",
+ "f='2001'-'2003-06-30' '2000'",
+ "f='2001'-'2003-06-30' \"2000\"",
+ "f= '2001' - '2003-06-30' 2000",
+ "f= '2001' - '2003-06-30' '2000'",
+ "f= '2001' - '2003-06-30' \"2000\"",
+ "f=\"2001\"-\"2003-06-30\" 2000",
+ "f=\"2001\"-\"2003-06-30\" '2000'",
+ "f=\"2001\"-\"2003-06-30\" \"2000\"",
+ "f= \"2001\" - \"2003-06-30\" 2000",
+ "f= \"2001\" - \"2003-06-30\" '2000'",
+ "f= \"2001\" - \"2003-06-30\" \"2000\"",
+ "f= 2001 - '2003-06-30' 2000",
+ "f= 2001 - '2003-06-30' '2000'",
+ "f= 2001 - '2003-06-30' \"2000\"",
+ "f= 2001 - \"2003-06-30\" 2000",
+ "f= 2001 - \"2003-06-30\" '2000'",
+ "f= 2001 - \"2003-06-30\" \"2000\""
+ ))
+ assertObjectEquals("[{f:'2000/12/31 23:59:59'},{f:'2001/01/01 00:00:00'},{f:'2003/06/30 23:59:59'}]", run(in, s), ws);
+ }
+
+ @Test
+ public void dateSearch_or1() throws Exception {
+ B[] in = B.create("2000-12-31", "2001-01-01", "2001-12-31", "2002-01-01");
+ for (String s : a(
+ "f=2001 2003 2005",
+ "f= 2001 2003 2005 ",
+ "f='2001' '2003' '2005'",
+ "f= '2001' '2003' '2005' ",
+ "f=\"2001\" \"2003\" \"2005\"",
+ "f= \"2001\" \"2003\" \"2005\" "
+ ))
+ assertObjectEquals("[{f:'2001/01/01 00:00:00'},{f:'2001/12/31 00:00:00'}]", run(in, s), ws);
+ }
+
+ @Test
+ public void dateSearch_or2() throws Exception {
+ B[] in = B.create("2002-12-31", "2003-01-01", "2003-12-31", "2004-01-01");
+ for (String s : a(
+ "f=2001 2003 2005",
+ "f= 2001 2003 2005 ",
+ "f='2001' '2003' '2005'",
+ "f= '2001' '2003' '2005' ",
+ "f=\"2001\" \"2003\" \"2005\"",
+ "f= \"2001\" \"2003\" \"2005\" "
+ ))
+ assertObjectEquals("[{f:'2003/01/01 00:00:00'},{f:'2003/12/31 00:00:00'}]", run(in, s), ws);
+ }
+
+ @Test
+ public void dateSearch_or3() throws Exception {
+ B[] in = B.create("2004-12-31", "2005-01-01", "2005-12-31", "2006-01-01");
+ for (String s : a(
+ "f=2001 2003 2005",
+ "f= 2001 2003 2005 ",
+ "f='2001' '2003' '2005'",
+ "f= '2001' '2003' '2005' ",
+ "f=\"2001\" \"2003\" \"2005\"",
+ "f= \"2001\" \"2003\" \"2005\" "
+ ))
+ assertObjectEquals("[{f:'2005/01/01 00:00:00'},{f:'2005/12/31 00:00:00'}]", run(in, s), ws);
+ }
+
+ @Test
+ public void dateSearch_or_singleAndRange() throws Exception {
+ B[] in = B.create("2000-12-31", "2001-01-01", "2002-12-31", "2003-01-01");
+ for (String s : a(
+ "f=2001 >2002",
+ "f= 2001 >2002 ",
+ "f='2001' >'2002'",
+ "f= '2001' >'2002' ",
+ "f=\"2001\" >\"2002\"",
+ "f= \"2001\" >\"2002\" ",
+ "f=>2002 2001",
+ "f= >2002 2001 ",
+ "f=>'2002' '2001'",
+ "f= >'2002' '2001' ",
+ "f=>\"2002\" \"2001\"",
+ "f= >\"2002\" \"2001\" ",
+ "f=2001 >=2003",
+ "f= 2001 >=2003 ",
+ "f='2001' >='2003'",
+ "f= '2001' >='2003' ",
+ "f=\"2001\" >=\"2003\"",
+ "f= \"2001\" >=\"2003\" ",
+ "f=>=2003 2001",
+ "f= >=2003 2001 ",
+ "f=>='2003' '2001'",
+ "f= >='2003' '2001' ",
+ "f=>=\"2003\" \"2001\"",
+ "f= >=\"2003\" \"2001\" "
+ ))
+ assertObjectEquals("[{f:'2001/01/01 00:00:00'},{f:'2003/01/01 00:00:00'}]", run(in, s), ws);
+ for (String s : a(
+ "f=<2001 2003",
+ "f= <2001 2003 ",
+ "f=<'2001' '2003'",
+ "f= <'2001' '2003' ",
+ "f=<\"2001\" \"2003\"",
+ "f= <\"2001\" \"2003\" ",
+ "f=2003 <2001",
+ "f= 2003 <2001 ",
+ "f='2003' <'2001'",
+ "f= '2003' <'2001' ",
+ "f=\"2003\" <\"2001\"",
+ "f= \"2003\" <\"2001\" ",
+ "f=<=2000 2003",
+ "f= <=2000 2003 ",
+ "f=<='2000' '2003'",
+ "f= <='2000' '2003' ",
+ "f=<=\"2000\" \"2003\"",
+ "f= <=\"2000\" \"2003\" ",
+ "f=2003 <=2000",
+ "f= 2003 <=2000 ",
+ "f='2003' <='2000'",
+ "f= '2003' <='2000' ",
+ "f=\"2003\" <=\"2000\"",
+ "f= \"2003\" <=\"2000\" "
+ ))
+ assertObjectEquals("[{f:'2000/12/31 00:00:00'},{f:'2003/01/01 00:00:00'}]", run(in, s), ws);
+ }
+
+ @Test
+ public void dateSearch_badSearches() throws Exception {
+ B[] in = B.create("2000-12-31");
+ String[] ss = new String[] {
+ "f=X","(S01)",
+ "f=>X","(S02)",
+ "f=<X","(S03)",
+ "f=>=X","(S04)",
+ "f='1'X","(S07)",
+ "f=2000 X","(S09)",
+ "f=2000-X","(S10)",
+ "f=>","(ES02)",
+ "f=<","(ES03)",
+ "f=>=","(ES04)",
+ "f='","(ES05)",
+ "f=\"","(ES06)",
+ "f=2000-","(ES10)",
+ "f=2000-'","(ES11)",
+ "f=2000-\"","(ES12)"
+ };
+
+ for (int i = 0; i < ss.length; i+=2) {
+ try {
+ run(in, ss[i]);
+ } catch (PatternException e) {
+ assertTrue("["+e.getLocalizedMessage()+"]!=["+ss[i]+"]", e.getLocalizedMessage().contains(ss[i+1]));
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Other data structures.
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void d2ListOfMaps() throws Exception {
+ List<Map<?,?>> in = AList.create(
+ AMap.create("f","foo"),
+ AMap.create("f","bar"),
+ null,
+ AMap.create(null,"qux"),
+ AMap.create("quux",null),
+ AMap.create(null,null)
+ );
+ assertObjectEquals("[{f:'foo'}]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d2SetOfMaps() throws Exception {
+ Set<Map<?,?>> in = ASet.create(
+ AMap.create("f","foo"),
+ AMap.create("f","bar"),
+ null,
+ AMap.create(null,"qux"),
+ AMap.create("quux",null),
+ AMap.create(null,null)
+ );
+ assertObjectEquals("[{f:'foo'}]", run(in, "f=foo"));
+ }
+
+
+ @Test
+ public void d2ArrayOfMaps() throws Exception {
+ Map<?,?>[] in = new Map[]{
+ AMap.create("f","foo"),
+ AMap.create("f","bar"),
+ null,
+ AMap.create(null,"qux"),
+ AMap.create("quux",null),
+ AMap.create(null,null)
+ };
+ assertObjectEquals("[{f:'foo'}]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d2ListOfObjects() throws Exception {
+ List<Object> in = AList.create(
+ AMap.create("f","foo"),
+ AMap.create("f","bar"),
+ null,
+ AMap.create(null,"qux"),
+ AMap.create("quux",null),
+ AMap.create(null,null),
+ "xxx",
+ 123
+ );
+ assertObjectEquals("[{f:'foo'}]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d2SetOfObjects() throws Exception {
+ Set<Object> in = ASet.create(
+ AMap.create("f","foo"),
+ AMap.create("f","bar"),
+ null,
+ AMap.create(null,"qux"),
+ AMap.create("quux",null),
+ AMap.create(null,null),
+ "xxx",
+ 123
+ );
+ assertObjectEquals("[{f:'foo'}]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d2ArrayOfObjects() throws Exception {
+ Object[] in = new Object[]{
+ AMap.create("f","foo"),
+ AMap.create("f","bar"),
+ null,
+ AMap.create(null,"qux"),
+ AMap.create("quux",null),
+ AMap.create(null,null),
+ "xxx",
+ 123
+ };
+ assertObjectEquals("[{f:'foo'}]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d2ListOfMapsWithLists() throws Exception {
+ List<Map<?,?>> in = AList.create(
+ AMap.create("f",AList.create("foo")),
+ AMap.create("f",AList.create("bar")),
+ null,
+ AMap.create(null,AList.create("qux")),
+ AMap.create("quux",AList.create((Object)null)),
+ AMap.create(null,AList.create((Object)null))
+ );
+ assertObjectEquals("[{f:['foo']}]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d2SetOfMapsWithSets() throws Exception {
+ Set<Map<?,?>> in = ASet.create(
+ AMap.create("f",ASet.create("foo")),
+ AMap.create("f",ASet.create("bar")),
+ null,
+ AMap.create(null,ASet.create("qux")),
+ AMap.create("quux",ASet.create((Object)null)),
+ AMap.create(null,ASet.create((Object)null))
+ );
+ assertObjectEquals("[{f:['foo']}]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d2ArrayOfMapsWithArrays() throws Exception {
+ Map<?,?>[] in = new Map[]{
+ AMap.create("f",new Object[]{"foo"}),
+ AMap.create("f",new Object[]{"bar"}),
+ null,
+ AMap.create(null,new Object[]{"qux"}),
+ AMap.create("quux",new Object[]{null}),
+ AMap.create(null,new Object[]{null})
+ };
+ assertObjectEquals("[{f:['foo']}]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d2ListOfBeans() throws Exception {
+ List<A> in = AList.create(
+ A.create("foo"),
+ A.create("bar"),
+ null,
+ A.create(null)
+ );
+ assertObjectEquals("[{f:'foo'}]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3ListOfListOfMaps() throws Exception {
+ List<List<Map<?,?>>> in = AList.create(
+ AList.create(AMap.create("f","foo")),
+ AList.create(AMap.create("f","bar")),
+ AList.create((Map<?,?>)null),
+ AList.create(AMap.create(null,"qux")),
+ AList.create(AMap.create("quux",null)),
+ AList.create(AMap.create(null,null)),
+ null
+ );
+ assertObjectEquals("[[{f:'foo'}]]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3SetOfSetOfMaps() throws Exception {
+ Set<Set<Map<?,?>>> in = ASet.create(
+ ASet.create(AMap.create("f","foo")),
+ ASet.create(AMap.create("f","bar")),
+ ASet.create(AMap.create("f","baz")),
+ ASet.create((Map<?,?>)null),
+ ASet.create(AMap.create(null,"qux")),
+ ASet.create(AMap.create("quux",null)),
+ ASet.create(AMap.create(null,null)),
+ null
+ );
+ assertObjectEquals("[[{f:'foo'}]]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3ArrayOfArrayOfMaps() throws Exception {
+ Map<?,?>[][] in = new Map[][]{
+ new Map[]{AMap.create("f","foo")},
+ new Map[]{AMap.create("f","bar")},
+ new Map[]{AMap.create("f","baz")},
+ new Map[]{null},
+ new Map[]{AMap.create(null,"qux")},
+ new Map[]{AMap.create("quux",null)},
+ new Map[]{AMap.create(null,null)},
+ null
+ };
+ assertObjectEquals("[[{f:'foo'}]]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3ListOfListOfObjects() throws Exception {
+ List<List<Object>> in = AList.create(
+ AList.create(AMap.create("f","foo")),
+ AList.create(AMap.create("f","bar")),
+ AList.create((Object)null),
+ AList.create(AMap.create(null,"qux")),
+ AList.create(AMap.create("quux",null)),
+ AList.create(AMap.create(null,null)),
+ AList.create("xxx"),
+ AList.create(123),
+ null
+ );
+ assertObjectEquals("[[{f:'foo'}]]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3SetOfSetOfObjects() throws Exception {
+ Set<Set<Object>> in = ASet.create(
+ ASet.create(AMap.create("f","foo")),
+ ASet.create(AMap.create("f","bar")),
+ ASet.create((Map<?,?>)null),
+ ASet.create(AMap.create(null,"qux")),
+ ASet.create(AMap.create("quux",null)),
+ ASet.create(AMap.create(null,null)),
+ ASet.create("xxx"),
+ ASet.create(123),
+ null
+ );
+ assertObjectEquals("[[{f:'foo'}]]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3ArrayOfArrayOfObjects() throws Exception {
+ Object[][] in = new Object[][]{
+ new Object[]{AMap.create("f","foo")},
+ new Object[]{AMap.create("f","bar")},
+ new Object[]{null},
+ new Object[]{AMap.create(null,"qux")},
+ new Object[]{AMap.create("quux",null)},
+ new Object[]{AMap.create(null,null)},
+ new Object[]{"xxx"},
+ new Object[]{123},
+ null
+ };
+ assertObjectEquals("[[{f:'foo'}]]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3ListOfListOfMapsWithCollections() throws Exception {
+ List<List<Map<?,?>>> in = AList.create(
+ AList.create(AMap.create("f",AList.create("foo"))),
+ AList.create(AMap.create("f",AList.create("bar"))),
+ AList.create((Map<?,?>)null),
+ AList.create(AMap.create(null,AList.create("qux"))),
+ AList.create(AMap.create("quux",AList.create((Object)null))),
+ AList.create(AMap.create(null,AList.create((Object)null))),
+ null
+ );
+ assertObjectEquals("[[{f:['foo']}]]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3SetOfSetOfMapsWithCollections() throws Exception {
+ Set<Set<Map<?,?>>> in = ASet.create(
+ ASet.create(AMap.create("f",ASet.create("foo"))),
+ ASet.create(AMap.create("f",ASet.create("bar"))),
+ ASet.create((Map<?,?>)null),
+ ASet.create(AMap.create(null,ASet.create("qux"))),
+ ASet.create(AMap.create("quux",ASet.create((Object)null))),
+ ASet.create(AMap.create(null,ASet.create((Object)null))),
+ null
+ );
+ assertObjectEquals("[[{f:['foo']}]]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3ArrayOfArrayOfMapsWithCollections() throws Exception {
+ Map<?,?>[][] in = new Map[][]{
+ new Map[]{AMap.create("f",new Object[]{"foo"})},
+ new Map[]{AMap.create("f",new Object[]{"bar"})},
+ new Map[]{null},
+ new Map[]{AMap.create(null,new Object[]{"qux"})},
+ new Map[]{AMap.create("quux",new Object[]{null})},
+ new Map[]{AMap.create(null,new Object[]{null})},
+ null
+ };
+ assertObjectEquals("[[{f:['foo']}]]", run(in, "f=foo"));
+ }
+
+ @Test
+ public void d3ArrayOfArrayOfBeans() throws Exception {
+ A[][] in = new A[][]{
+ new A[]{A.create("foo")},
+ new A[]{A.create("bar")},
+ new A[]{null},
+ new A[]{A.create(null)},
+ null
+ };
+ assertObjectEquals("[[{f:'foo'}]]", run(in, "f=foo"));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Other tests
+ //-----------------------------------------------------------------------------------------------------------------
+
+// @Test
+// public void noSearchArgs() {
+// SearchArgs sa = new SearchArgs();
+// assertObjectEquals("'foo'", run("foo", sa));
+// }
+//
+// @Test
+// public void invalidSearchArgs() {
+// for (String s : a("", "x")) {
+// try {
+// run("foo", s);
+// fail();
+// } catch (PatternException e) {
+// assertTrue(e.getLocalizedMessage().contains("Invalid search terms"));
+// }
+// }
+// SearchArgs sa = new SearchArgs();
+// assertObjectEquals("'foo'", run("foo", sa));
+// }
+//
+// @Test
+// public void not2dPojo() {
+// assertObjectEquals("'foo'", run("foo", "x=y"));
+// }
+//
+// @Test
+// public void nullInput() {
+// assertObjectEquals("null", run(null, "x=y"));
+// }
+//
+// @Test
+// public void searchArgsEmptyKey() {
+// SearchArgs sa = new SearchArgs().append(null, "foo");
+// assertObjectEquals("'foo'", run("foo", sa));
+// }
+//
+// @Test
+// public void searchArgsEmptyValue() {
+// SearchArgs sa = new SearchArgs().append("foo", null);
+// assertObjectEquals("'foo'", run("foo", sa));
+// }
+}
\ No newline at end of file
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/pojotools/PojoSorterTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/pojotools/PojoSorterTest.java
new file mode 100644
index 0000000..c164fc3
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/pojotools/PojoSorterTest.java
@@ -0,0 +1,265 @@
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import static org.apache.juneau.testutils.TestUtils.*;
+import static org.junit.Assert.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.utils.*;
+import org.junit.*;
+
+/**
+ * Tests the PojoPaginator class.
+ */
+public class PojoSorterTest {
+
+ PojoSorter p = new PojoSorter();
+ BeanSession bs = BeanContext.DEFAULT.createBeanSession();
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Null input
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void nullInput() {
+ assertNull(p.run(bs, null, null));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Arrays
+ //-----------------------------------------------------------------------------------------------------------------
+
+ public static class A {
+ public String f;
+
+ public static A create(String f) {
+ A a = new A();
+ a.f = f;
+ return a;
+ }
+ }
+
+ @Test
+ public void beanArray() {
+ Object in = new A[]{A.create("c"),A.create("a"),A.create("b"),A.create("e"),A.create("d")};
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[{f:'a'},{f:'b'},{f:'c'},{f:'d'},{f:'e'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanArray_reverse() {
+ Object in = new A[]{A.create("c"),A.create("a"),A.create("b"),A.create("e"),A.create("d")};
+ SortArgs sa = new SortArgs("f-");
+ assertObjectEquals("[{f:'e'},{f:'d'},{f:'c'},{f:'b'},{f:'a'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanArrayContainingNulls() {
+ Object in = new A[]{A.create("c"),A.create("a"),null,null,A.create("b")};;
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[null,null,{f:'a'},{f:'b'},{f:'c'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanArrayContainingDups() {
+ Object in = new A[]{A.create("c"),A.create("a"),null,A.create("a"),A.create("b")};
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[null,{f:'a'},{f:'a'},{f:'b'},{f:'c'}]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Lists
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void beanList() {
+ Object in = AList.create(A.create("c"),A.create("a"),A.create("b"),A.create("e"),A.create("d"));
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[{f:'a'},{f:'b'},{f:'c'},{f:'d'},{f:'e'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanList_reverse() {
+ Object in = AList.create(A.create("c"),A.create("a"),A.create("b"),A.create("e"),A.create("d"));
+ SortArgs sa = new SortArgs("f-");
+ assertObjectEquals("[{f:'e'},{f:'d'},{f:'c'},{f:'b'},{f:'a'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanListContainingNull() {
+ Object in = AList.create(A.create("c"),A.create("a"),null,null,A.create("b"));
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[null,null,{f:'a'},{f:'b'},{f:'c'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanListContainingDups() {
+ Object in = AList.create(A.create("c"),A.create("a"),null,A.create("a"),A.create("b"));
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[null,{f:'a'},{f:'a'},{f:'b'},{f:'c'}]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Sets
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void beanSet() {
+ Object in = ASet.create(A.create("c"),A.create("a"),A.create("b"),A.create("e"),A.create("d"));
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[{f:'a'},{f:'b'},{f:'c'},{f:'d'},{f:'e'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void betSet_reverse() {
+ Object in = ASet.create(A.create("c"),A.create("a"),A.create("b"),A.create("e"),A.create("d"));
+ SortArgs sa = new SortArgs("f-");
+ assertObjectEquals("[{f:'e'},{f:'d'},{f:'c'},{f:'b'},{f:'a'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanSetContainingNull() {
+ Object in = ASet.create(A.create("c"),A.create("a"),null,null,A.create("b"));
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[null,{f:'a'},{f:'b'},{f:'c'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanSetContainingDups() {
+ Object in = ASet.create(A.create("c"),A.create("a"),null,A.create("a"),A.create("b"));
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[null,{f:'a'},{f:'a'},{f:'b'},{f:'c'}]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Other
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void emptySort() {
+ Object in = ASet.create(A.create("c"),A.create("a"),A.create("b"));
+ SortArgs sa = new SortArgs();
+ assertObjectEquals("[{f:'c'},{f:'a'},{f:'b'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void invalidDataType() {
+ Object in = AMap.create("a","b");
+ SortArgs sa = new SortArgs("x");
+ in = p.run(bs, in, sa);
+ assertObjectEquals("{a:'b'}", in);
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Lists of Maps
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void listOfMaps() {
+ Object in = AList.create(AMap.create("f","c"),AMap.create("f","a"),AMap.create("f","b"),AMap.create("f","e"),AMap.create("f","d"));
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[{f:'a'},{f:'b'},{f:'c'},{f:'d'},{f:'e'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void listOfMaps_reverse() {
+ Object in = AList.create(AMap.create("f","c"),AMap.create("f","a"),AMap.create("f","b"),AMap.create("f","e"),AMap.create("f","d"));
+ SortArgs sa = new SortArgs("f-");
+ assertObjectEquals("[{f:'e'},{f:'d'},{f:'c'},{f:'b'},{f:'a'}]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Lists of Other
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void listOfOther() {
+ Object in = AList.create(AList.create("c"),AList.create("a"),AList.create("b"));
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[['c'],['a'],['b']]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void listOfOther_reverse() {
+ Object in = AList.create(AList.create("c"),AList.create("a"),AList.create("b"));
+ SortArgs sa = new SortArgs("f-");
+ assertObjectEquals("[['c'],['a'],['b']]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Other
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void nonExistentField() {
+ Object in = new A[]{A.create("c"),A.create("a"),A.create("b"),A.create("e"),A.create("d")};
+ SortArgs sa = new SortArgs("fx");
+ assertObjectEquals("[{f:'c'},{f:'a'},{f:'b'},{f:'e'},{f:'d'}]", p.run(bs, in, sa));
+ }
+
+ public static class B {
+ public Object f;
+
+ public static B create(Object f) {
+ B b = new B();
+ b.f = f;
+ return b;
+ }
+ }
+
+ // Should gracefully handle different sorting data types.
+ @Test
+ public void mixtureOfTypes() {
+ Object in = new B[]{B.create(1),B.create(true),B.create("a")};
+ SortArgs sa = new SortArgs("f");
+ assertObjectEquals("[{f:1},{f:true},{f:'a'}]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Sort by multiple columns.
+ //-----------------------------------------------------------------------------------------------------------------
+
+ public static class C {
+ public int f1;
+ public float f2;
+
+ public static C create(int f1, float f2) {
+ C c = new C();
+ c.f1 = f1;
+ c.f2 = f2;
+ return c;
+ }
+ }
+
+ @Test
+ public void sortMultipleColumns() {
+ Object in = new C[]{C.create(1,1),C.create(3,2),C.create(3,1),C.create(2,1),C.create(2,2)};
+ SortArgs sa = new SortArgs("f1","f2");
+ assertObjectEquals("[{f1:1,f2:1.0},{f1:2,f2:1.0},{f1:2,f2:2.0},{f1:3,f2:1.0},{f1:3,f2:2.0}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void sortMultipleColumns_descending() {
+ Object in = new C[]{C.create(1,1),C.create(3,2),C.create(3,1),C.create(2,1),C.create(2,2)};
+ SortArgs sa = new SortArgs("f1-","f2-");
+ assertObjectEquals("[{f1:3,f2:2.0},{f1:3,f2:1.0},{f1:2,f2:2.0},{f1:2,f2:1.0},{f1:1,f2:1.0}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void sortMultipleColumns_ascendingAndDescending() {
+ Object in = new C[]{C.create(1,1),C.create(3,2),C.create(3,1),C.create(2,1),C.create(2,2)};
+ SortArgs sa = new SortArgs("f1-","f2+");
+ assertObjectEquals("[{f1:3,f2:1.0},{f1:3,f2:2.0},{f1:2,f2:1.0},{f1:2,f2:2.0},{f1:1,f2:1.0}]", p.run(bs, in, sa));
+ }
+}
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/pojotools/PojoViewerTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/pojotools/PojoViewerTest.java
new file mode 100644
index 0000000..f4e3875
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/pojotools/PojoViewerTest.java
@@ -0,0 +1,349 @@
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import static org.apache.juneau.testutils.TestUtils.*;
+import static org.junit.Assert.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.utils.*;
+import org.junit.*;
+
+/**
+ * Tests the PojoPaginator class.
+ */
+public class PojoViewerTest {
+
+ PojoViewer p = new PojoViewer();
+ BeanSession bs = BeanContext.DEFAULT.createBeanSession();
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Null input
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void nullInput() {
+ assertNull(p.run(bs, null, null));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Simple bean
+ //-----------------------------------------------------------------------------------------------------------------
+
+ public static class A {
+ public String f1,f2;
+
+ public static A create(String f1, String f2) {
+ A a = new A();
+ a.f1 = f1;
+ a.f2 = f2;
+ return a;
+ }
+ }
+
+ @Test
+ public void simpleBean() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = A.create("x1","x2");
+ assertObjectEquals("{f1:'x1'}", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void simpleBean_reverseColumns() {
+ ViewArgs sa = new ViewArgs("f2","f1");
+ Object in = A.create("x1","x2");
+ assertObjectEquals("{f2:'x2',f1:'x1'}", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void simpleBean_dupColumns() {
+ ViewArgs sa = new ViewArgs("f1","f1");
+ Object in = A.create("x1","x2");
+ assertObjectEquals("{f1:'x1'}", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void simpleBean_nonExistentColumns() {
+ ViewArgs sa = new ViewArgs("fx");
+ Object in = A.create("x1","x2");
+ assertObjectEquals("{}", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void simpleBean_nullColumn() {
+ ViewArgs sa = new ViewArgs("f1",null);
+ Object in = A.create("x1","x2");
+ assertObjectEquals("{f1:'x1'}", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void simpleBean_emptyArgs() {
+ ViewArgs sa = new ViewArgs();
+ Object in = A.create("x1","x2");
+ assertObjectEquals("{}", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Simple BeanMap
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void simpleBeanMap() {
+ ViewArgs sa = new ViewArgs("f1");
+ Object in = bs.toBeanMap(A.create("x1","x2"));
+ assertObjectEquals("{f1:'x1'}", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Simple map
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void simpleMap() {
+ ViewArgs sa = new ViewArgs("f1");
+ Object in = AMap.create().append("f1","x1").append("f2","x2");
+ assertObjectEquals("{f1:'x1'}", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void simpleMap_reverseColumns() {
+ ViewArgs sa = new ViewArgs("f2","f1");
+ Object in = AMap.create().append("f1","x1").append("f2","x2");
+ assertObjectEquals("{f2:'x2',f1:'x1'}", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void simpleMap_nonExistentColumns() {
+ ViewArgs sa = new ViewArgs("fx");
+ Object in = AMap.create().append("f1","x1").append("f2","x2");
+ assertObjectEquals("{}", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void simpleMap_nullColumn() {
+ ViewArgs sa = new ViewArgs("f1",null);
+ Object in = AMap.create().append("f1","x1").append("f2","x2");
+ assertObjectEquals("{f1:'x1'}", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void simpleMap_emptyView() {
+ ViewArgs sa = new ViewArgs();
+ Object in = AMap.create().append("f1","x1").append("f2","x2");
+ assertObjectEquals("{}", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Bean array
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void beanArray() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = new A[]{A.create("x1","x2")};
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanArray_reverseColumns() {
+ ViewArgs sa = new ViewArgs("f2","f1");
+ Object in = new A[]{A.create("x1","x2")};
+ assertObjectEquals("[{f2:'x2',f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanArray_dupColumns() {
+ ViewArgs sa = new ViewArgs("f1","f1");
+ Object in = new A[]{A.create("x1","x2")};
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanArray_nonExistentColumns() {
+ ViewArgs sa = new ViewArgs("fx");
+ Object in = new A[]{A.create("x1","x2")};
+ assertObjectEquals("[{}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanArray_nullColumn() {
+ ViewArgs sa = new ViewArgs("f1",null);
+ Object in = new A[]{A.create("x1","x2")};
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanArray_emptyArgs() {
+ ViewArgs sa = new ViewArgs();
+ Object in = new A[]{A.create("x1","x2")};
+ assertObjectEquals("[{}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanArray_withNull() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = new A[]{A.create("x1","x2"),null};
+ assertObjectEquals("[{f1:'x1'},null]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Bean list
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void beanList() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = AList.create(A.create("x1","x2"));
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanList_reverseColumns() {
+ ViewArgs sa = new ViewArgs("f2","f1");
+ Object in = AList.create(A.create("x1","x2"));
+ assertObjectEquals("[{f2:'x2',f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanList_dupColumns() {
+ ViewArgs sa = new ViewArgs("f1","f1");
+ Object in = AList.create(A.create("x1","x2"));
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanList_nonExistentColumns() {
+ ViewArgs sa = new ViewArgs("fx");
+ Object in = AList.create(A.create("x1","x2"));
+ assertObjectEquals("[{}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanList_nullColumn() {
+ ViewArgs sa = new ViewArgs("f1",null);
+ Object in = AList.create(A.create("x1","x2"));
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanList_emptyArgs() {
+ ViewArgs sa = new ViewArgs();
+ Object in = AList.create(A.create("x1","x2"));
+ assertObjectEquals("[{}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanList_withNull() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = AList.create(A.create("x1","x2"),null);
+ assertObjectEquals("[{f1:'x1'},null]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Bean set
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void beanSet() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = ASet.create(A.create("x1","x2"));
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanSet_reverseColumns() {
+ ViewArgs sa = new ViewArgs("f2","f1");
+ Object in = ASet.create(A.create("x1","x2"));
+ assertObjectEquals("[{f2:'x2',f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanSet_dupColumns() {
+ ViewArgs sa = new ViewArgs("f1","f1");
+ Object in = ASet.create(A.create("x1","x2"));
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanSet_nonExistentColumns() {
+ ViewArgs sa = new ViewArgs("fx");
+ Object in = ASet.create(A.create("x1","x2"));
+ assertObjectEquals("[{}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanSet_nullColumn() {
+ ViewArgs sa = new ViewArgs("f1",null);
+ Object in = ASet.create(A.create("x1","x2"));
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanSet_emptyArgs() {
+ ViewArgs sa = new ViewArgs();
+ Object in = ASet.create(A.create("x1","x2"));
+ assertObjectEquals("[{}]", p.run(bs, in, sa));
+ }
+
+ @Test
+ public void beanSet_withNull() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = ASet.create(A.create("x1","x2"),null);
+ assertObjectEquals("[{f1:'x1'},null]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Other object
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void otherObject() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = "foobar";
+ assertObjectEquals("'foobar'", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Map list
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void mapList() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = AList.create(AMap.create().append("f1","x1").append("f2","x2"));
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // BeanMap list
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void beanMapList() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = AList.create(bs.toBeanMap(A.create("x1","x2")));
+ assertObjectEquals("[{f1:'x1'}]", p.run(bs, in, sa));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Other object list
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void otherObjectList() {
+ ViewArgs sa = new ViewArgs("f1");;
+ Object in = AList.create("foobar");
+ assertObjectEquals("['foobar']", p.run(bs, in, sa));
+ }
+
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateBeanMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateBeanMap.java
index 1af9076..4b8a8b3 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateBeanMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateBeanMap.java
@@ -28,7 +28,7 @@ import org.apache.juneau.*;
*/
public class DelegateBeanMap<T> extends BeanMap<T> {
- private Set<String> keys = Collections.newSetFromMap(new LinkedHashMap<String,Boolean>());
+ private Set<String> keys = new LinkedHashSet<>();
private ObjectMap overrideValues = new ObjectMap();
/**
@@ -78,10 +78,12 @@ public class DelegateBeanMap<T> extends BeanMap<T> {
* This does not affect the underlying bean.
*
* @param keys The remaining keys in the bean map (in the specified order).
+ * @return This object (for method chaining).
*/
- public void filterKeys(List<String> keys) {
+ public DelegateBeanMap<T> filterKeys(List<String> keys) {
this.keys.clear();
this.keys.addAll(keys);
+ return this;
}
@Override /* Map */
@@ -119,7 +121,7 @@ public class DelegateBeanMap<T> extends BeanMap<T> {
if (overrideValues.containsKey(key))
p = BeanPropertyMeta.builder(this.meta, key).overrideValue(overrideValues.get(key)).delegateFor(p).build();
if (p == null)
- throw new BeanRuntimeException(super.getClassMeta().getInnerClass(), "Property ''{0}'' not found on class.", key);
+ p = BeanPropertyMeta.builder(this.meta, key).overrideValue(null).delegateFor(p).build();
l.add(p);
}
return l;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java
index 83d7440..daaf02c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java
@@ -21,7 +21,8 @@ import org.apache.juneau.*;
*
* @param <T> The class type of the wrapped bean.
*/
-public class DelegateMap<T> extends ObjectMap implements Delegate<T> {
+@SuppressWarnings("rawtypes")
+public class DelegateMap<T extends Map> extends ObjectMap implements Delegate<T> {
private static final long serialVersionUID = 1L;
private transient ClassMeta<T> classMeta;
@@ -29,10 +30,13 @@ public class DelegateMap<T> extends ObjectMap implements Delegate<T> {
/**
* Constructor.
*
- * @param classMeta The metadata object that created this delegate object.
+ * @param m The metadata object that created this delegate object.
+ * @param session
*/
- public DelegateMap(ClassMeta<T> classMeta) {
- this.classMeta = classMeta;
+ public DelegateMap(T m, BeanSession session) {
+ this.classMeta = session.getClassMetaForObject(m);
+ for (Map.Entry e : (Set<Map.Entry>)m.entrySet())
+ put(StringUtils.asString(e.getKey()), e.getValue());
}
@Override /* Delegate */
@@ -47,12 +51,15 @@ public class DelegateMap<T> extends ObjectMap implements Delegate<T> {
* This does not affect the underlying map.
*
* @param keys The remaining keys in the map (in the specified order).
+ * @return This object (for method chaining).
*/
- public void filterKeys(List<String> keys) {
+ public DelegateMap<T> filterKeys(List<String> keys) {
ObjectMap m2 = new ObjectMap();
for (String k : keys)
- m2.put(k, get(k));
+ if (containsKey(k))
+ m2.put(k, get(k));
this.clear();
this.putAll(m2);
+ return this;
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ObjectUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ObjectUtils.java
index 25f225d..aa6d54f 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ObjectUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ObjectUtils.java
@@ -350,4 +350,32 @@ public final class ObjectUtils {
return oo;
return null;
}
+
+ /**
+ * Compares two objects for equality.
+ *
+ * <p>
+ * Nulls are always considered less-than unless both are null.
+ *
+ * @param o1 Object 1.
+ * @param o2 Object 2.
+ * @return
+ * <code>-1</code>, <code>0</code>, or <code>1</code> if <code>o1</code> is less-than, equal, or greater-than <code>o2</code>.
+ * <br><code>0</code> if objects are not of the same type or do not implement the {@link Comparable} interface.
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public static int compare(Object o1, Object o2) {
+ if (o1 == null) {
+ if (o2 == null)
+ return 0;
+ return -1;
+ } else if (o2 == null) {
+ return 1;
+ }
+
+ if (o1.getClass() == o2.getClass() && o1 instanceof Comparable)
+ return ((Comparable)o1).compareTo(o2);
+
+ return 0;
+ }
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StateMachineState.java
similarity index 65%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StateMachineState.java
index 5231a47..9232bff 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StateMachineState.java
@@ -10,33 +10,12 @@
// * "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.rest.widget;
-
-import org.apache.juneau.*;
-import org.apache.juneau.rest.*;
+package org.apache.juneau.internal;
/**
- * Widget that places a powered-by-Juneau message on the page.
- *
- * <p>
- * The variable it resolves is <js>"$W{PoweredByJuneau}"</js>.
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- * <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.PredefinedWidgets}
- * </ul>
+ * Enums for state-machine states.
*/
-public class PoweredByJuneau extends Widget {
-
-
- /**
- * Returns an Apache Juneau image tag hyperlinked to <js>"http://juneau.apache.org"</js>
- */
- @Override /* Widget */
- public String getHtml(RestRequest req) throws Exception {
- UriResolver r = req.getUriResolver();
- return "<a href='http://juneau.apache.org'><img style='float:right;padding-right:20px;height:32px' src='"+r.resolve("servlet:/htdocs/images/juneau.png")+"'>";
- }
+@SuppressWarnings("javadoc")
+public enum StateMachineState {
+ S01, S02, S03, S04, S05, S06, S07, S08, S09, S10, S11, S12, S13, S14, S15, S16, S17, S18, S19, S20;
}
-
-
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index fa8568e..c166d25 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -772,6 +772,19 @@ public final class StringUtils {
* <br>An empty string results in an empty array.
*/
public static String[] splitQuoted(String s) {
+ return splitQuoted(s, false);
+ }
+
+ /**
+ * Same as {@link #splitQuoted(String)} but allows you to optionally keep the quote characters.
+ *
+ * @param s The input string.
+ * @param keepQuotes If <jk>true</jk>, quote characters are kept on the tokens.
+ * @return
+ * The results, or <jk>null</jk> if the input was <jk>null</jk>.
+ * <br>An empty string results in an empty array.
+ */
+ public static String[] splitQuoted(String s, boolean keepQuotes) {
if (s == null)
return null;
@@ -802,10 +815,10 @@ public final class StringUtils {
if (state == S1) {
if (c == '\'') {
state = S2;
- mark = i+1;
+ mark = keepQuotes ? i : i+1;
} else if (c == '"') {
state = S3;
- mark = i+1;
+ mark = keepQuotes ? i : i+1;
} else if (c != ' ' && c != '\t') {
state = S4;
mark = i;
@@ -813,10 +826,10 @@ public final class StringUtils {
} else if (state == S2 || state == S3) {
if (c == '\\') {
isInEscape = ! isInEscape;
- needsUnescape = true;
+ needsUnescape = ! keepQuotes;
} else if (! isInEscape) {
if (c == (state == S2 ? '\'' : '"')) {
- String s2 = s.substring(mark, i);
+ String s2 = s.substring(mark, keepQuotes ? i+1 : i);
if (needsUnescape)
s2 = unEscapeChars(s2, QUOTE_ESCAPE_SET);
l.add(s2);
@@ -1285,6 +1298,18 @@ public final class StringUtils {
}
/**
+ * Strips the first and last character from a string.
+ *
+ * @param s The string to strip.
+ * @return The striped string, or the same string if the input was <jk>null</jk> or less than length 2.
+ */
+ public static String strip(String s) {
+ if (s == null || s.length() <= 1)
+ return s;
+ return s.substring(1, s.length()-1);
+ }
+
+ /**
* Parses an ISO8601 string into a date.
*
* <p>
@@ -2584,4 +2609,47 @@ public final class StringUtils {
}
return l;
}
+
+ /**
+ * Replaces tokens in a string with a different token.
+ *
+ * <p>
+ * replace("A and B and C", "and", "or") -> "A or B or C"
+ * replace("andandand", "and", "or") -> "ororor"
+ * replace(null, "and", "or") -> null
+ * replace("andandand", null, "or") -> "andandand"
+ * replace("andandand", "", "or") -> "andandand"
+ * replace("A and B and C", "and", null) -> "A B C"
+ *
+ * @param s The string to replace characters in.
+ * @param from The character to replace.
+ * @param to The character to replace with.
+ * @param ignoreEscapedChars Specify 'true' if escaped 'from' characters should be ignored.
+ * @return The string with characters replaced.
+ */
+ public static String replaceChars(String s, char from, char to, boolean ignoreEscapedChars) {
+ if (s == null)
+ return null;
+
+ if (s.indexOf(from) == -1)
+ return s;
+
+ char[] sArray = s.toCharArray();
+
+ int escapeCount = 0;
+ int singleQuoteCount = 0;
+ int doubleQuoteCount = 0;
+ for (int i = 0; i < sArray.length; i++) {
+ char c = sArray[i];
+ if (c == '\\' && ignoreEscapedChars)
+ escapeCount++;
+ else if (escapeCount % 2 == 0) {
+ if (c == from && singleQuoteCount % 2 == 0 && doubleQuoteCount % 2 == 0)
+ sArray[i] = to;
+ }
+ if (sArray[i] != '\\') escapeCount = 0;
+ }
+ return new String(sArray);
+ }
+
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/Equality.java
similarity index 65%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/Equality.java
index 5231a47..b567988 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/Equality.java
@@ -10,33 +10,12 @@
// * "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.rest.widget;
-
-import org.apache.juneau.*;
-import org.apache.juneau.rest.*;
+package org.apache.juneau.pojotools;
/**
- * Widget that places a powered-by-Juneau message on the page.
- *
- * <p>
- * The variable it resolves is <js>"$W{PoweredByJuneau}"</js>.
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- * <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.PredefinedWidgets}
- * </ul>
+ * TODO
*/
-public class PoweredByJuneau extends Widget {
-
-
- /**
- * Returns an Apache Juneau image tag hyperlinked to <js>"http://juneau.apache.org"</js>
- */
- @Override /* Widget */
- public String getHtml(RestRequest req) throws Exception {
- UriResolver r = req.getUriResolver();
- return "<a href='http://juneau.apache.org'><img style='float:right;padding-right:20px;height:32px' src='"+r.resolve("servlet:/htdocs/images/juneau.png")+"'>";
- }
-}
-
-
+@SuppressWarnings("javadoc")
+public enum Equality {
+ GT, GTE, LT, LTE, NONE
+}
\ No newline at end of file
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/Matcher.java
similarity index 67%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/Matcher.java
index 5231a47..34c125f 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/Matcher.java
@@ -10,33 +10,21 @@
// * "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.rest.widget;
+package org.apache.juneau.pojotools;
import org.apache.juneau.*;
-import org.apache.juneau.rest.*;
/**
- * Widget that places a powered-by-Juneau message on the page.
- *
- * <p>
- * The variable it resolves is <js>"$W{PoweredByJuneau}"</js>.
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- * <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.PredefinedWidgets}
- * </ul>
+ * Common interface for matchers used by the {@link PojoSearcher} class.
*/
-public class PoweredByJuneau extends Widget {
-
+public abstract class Matcher {
/**
- * Returns an Apache Juneau image tag hyperlinked to <js>"http://juneau.apache.org"</js>
+ * Returns <jk>true</jk> if this matcher matches the specified object..
+ *
+ * @param cm The class type of the object being matched. Never <jk>null</jk>.
+ * @param o The object being matched. Never <jk>null</jk>.
+ * @return <jk>true</jk> if the specified object matches the specified pattern.
*/
- @Override /* Widget */
- public String getHtml(RestRequest req) throws Exception {
- UriResolver r = req.getUriResolver();
- return "<a href='http://juneau.apache.org'><img style='float:right;padding-right:20px;height:32px' src='"+r.resolve("servlet:/htdocs/images/juneau.png")+"'>";
- }
+ public abstract boolean matches(ClassMeta<?> cm, Object o);
}
-
-
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/MatcherFactory.java
similarity index 67%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/MatcherFactory.java
index 5231a47..511b76d 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/MatcherFactory.java
@@ -10,33 +10,28 @@
// * "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.rest.widget;
+package org.apache.juneau.pojotools;
import org.apache.juneau.*;
-import org.apache.juneau.rest.*;
/**
- * Widget that places a powered-by-Juneau message on the page.
- *
- * <p>
- * The variable it resolves is <js>"$W{PoweredByJuneau}"</js>.
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- * <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.PredefinedWidgets}
- * </ul>
+ * Common interface for matchers used by the {@link PojoSearcher} class.
*/
-public class PoweredByJuneau extends Widget {
+public abstract class MatcherFactory {
+ /**
+ * Returns <jk>true</jk> if this matcher can be used on the specified object.
+ *
+ * @param cm The class type of the object being matched. Never <jk>null</jk>.
+ * @return <jk>true</jk> if this matcher can be used on the specified object.
+ */
+ public abstract boolean canMatch(ClassMeta<?> cm);
/**
- * Returns an Apache Juneau image tag hyperlinked to <js>"http://juneau.apache.org"</js>
+ * Instantiates a matcher for the specified pattern.
+ *
+ * @param pattern The pattern string.
+ * @return A matcher for the specified pattern.
*/
- @Override /* Widget */
- public String getHtml(RestRequest req) throws Exception {
- UriResolver r = req.getUriResolver();
- return "<a href='http://juneau.apache.org'><img style='float:right;padding-right:20px;height:32px' src='"+r.resolve("servlet:/htdocs/images/juneau.png")+"'>";
- }
+ public abstract Matcher create(String pattern);
}
-
-
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/NumberMatcherFactory.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/NumberMatcherFactory.java
new file mode 100644
index 0000000..0600397
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/NumberMatcherFactory.java
@@ -0,0 +1,291 @@
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import static org.apache.juneau.internal.StateMachineState.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * TODO
+ *
+ */
+public class NumberMatcherFactory extends MatcherFactory {
+
+ /**
+ * Default reusable matcher.
+ */
+ public static final NumberMatcherFactory DEFAULT = new NumberMatcherFactory();
+
+ @Override
+ public boolean canMatch(ClassMeta<?> cm) {
+ return cm.isNumber();
+ }
+
+ @Override
+ public Matcher create(String pattern) {
+ return new NumberMatcher(pattern);
+ }
+
+ /**
+ * A construct representing a single search pattern.
+ */
+ private static class NumberMatcher extends Matcher {
+ NumberRange[] numberRanges;
+ private static final AsciiSet
+ SNUM = AsciiSet.create("-0123456789."),
+ NUM = AsciiSet.create("0123456789."),
+ WS = AsciiSet.create(" \t");
+
+ public NumberMatcher(String s) {
+
+ s = s.trim();
+
+ List<NumberRange> l = new LinkedList<>();
+
+ // Possible patterns:
+ // 123, >123, <123, >=123, <=123, >-123, >=-123, 123-456, -123--456, !123, !123-456, 123 - 456 (one token), 123 -456 (two separate tokens)
+
+ // Possible states:
+ // S01 = Looking for start (WS=S01, [!]=S01, [>]=S02, [<]=S03, SNUM=S06)
+ // S02 = Found [>], looking for [=]/SNUM ([=]=S04, WS=S05, SNUM=S06)
+ // S03 = Found [<], looking for [=]/SNUM ([=]=S05, WS=S05, SNUM=S06)
+ // S04 = Found [=], looking for SNUN (WS=S05, SNUM=S06)
+ // S05 = Found [... ], looking for SNUM (SNUM=S06)
+ // S06 = Found [1], looking for [-]/WS (WS=S07, [-]=S08)
+ // S07 = Found [123 ], looking for [-]/SNUM (if -, could be 123 - 456 or 123 -456) ([-]=S09, SNUM=S07)
+ // S08 = Found [123-], looking for SNUM (Could be 123- 456 or 123-456) (SNUM=S11)
+ // S09 = Found [123 -], looking for WS/SNUM (If WS, then it's 123 - 456, otherwise 123 -456) (WS=S10, SNUM=S06)
+ // S10 = Found [123 - ], looking for SNUM (SNUM=S12)
+ // S11 = Found [123 - 4], looking for WS (WS=S01)
+
+ StateMachineState state = S01;
+ int mark = 0;
+ boolean isNot = false;
+ Equality eq = Equality.NONE;
+ Integer n1 = null, n2 = null;
+
+ int i;
+ for (i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (state == S01) {
+ if (c == '!') {
+ isNot = true;
+ } else if (WS.contains(c)) {
+ state = S01;
+ } else if (c == '>') {
+ state = S02;
+ eq = Equality.GT;
+ } else if (c == '<') {
+ state = S03;
+ eq = Equality.LT;
+ } else if (SNUM.contains(c)) {
+ state = S06;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S02) {
+ if (c == '=') {
+ state = S04;
+ eq = Equality.GTE;
+ } else if (WS.contains(c)) {
+ state = S05;
+ } else if (SNUM.contains(c)) {
+ state = S06;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S03) {
+ if (c == '=') {
+ state = S04;
+ eq = Equality.LTE;
+ } else if (WS.contains(c)) {
+ state = S05;
+ } else if (SNUM.contains(c)) {
+ state = S06;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S04) {
+ if (WS.contains(c)) {
+ state = S05;
+ } else if (SNUM.contains(c)) {
+ mark = i;
+ state = S06;
+ } else {
+ break;
+ }
+ } else if (state == S05) {
+ if (WS.contains(c)) {
+ state = S05;
+ } else if (SNUM.contains(c)) {
+ state = S06;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S06) {
+ if (NUM.contains(c)) {
+ state = S06;
+ } else if (WS.contains(c)) {
+ state = S07;
+ n1 = Integer.parseInt(s.substring(mark, i));
+ } else if (c == '-') {
+ state = S08;
+ n1 = Integer.parseInt(s.substring(mark, i));
+ } else {
+ break;
+ }
+ } else if (state == S07) {
+ if (WS.contains(c)) {
+ state = S07;
+ } else if (c == '-') {
+ state = S09;
+ } else if (SNUM.contains(c)) {
+ state = S06;
+ l.add(new NumberRange(eq, n1, isNot));
+ eq = Equality.NONE;
+ n1 = null;
+ isNot = false;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S08) {
+ if (WS.contains(c)) {
+ state = S08;
+ } else if (SNUM.contains(c)) {
+ state = S11;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S09) {
+ if (WS.contains(c)) {
+ state = S10;
+ } else if (NUM.contains(c)) {
+ state = S06;
+ l.add(new NumberRange(eq, n1, isNot));
+ eq = Equality.NONE;
+ n1 = null;
+ isNot = false;
+ mark = i-1;
+ } else {
+ break;
+ }
+ } else if (state == S10) {
+ if (WS.contains(c)) {
+ state = S10;
+ } else if (SNUM.contains(c)) {
+ state = S11;
+ mark = i;
+ } else {
+ break;
+ }
+ } else /* (state == S11) */ {
+ if (SNUM.contains(c)) {
+ state = S11;
+ } else if (WS.contains(c)) {
+ state = S01;
+ n2 = Integer.parseInt(s.substring(mark, i));
+ l.add(new NumberRange(eq, n1, n2, isNot));
+ eq = Equality.NONE;
+ n1 = n2 = null;
+ isNot = false;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (i != s.length())
+ throw new PatternException("Invalid range pattern ({0}): {1}", state, s);
+
+ if (state == S01) {
+ // No tokens found.
+ } else if (state == S02 || state == S03 || state == S04 || state == S08 || state == S09) {
+ throw new PatternException("Invalid range pattern (E{0}): {1}", state, s);
+ } else if (state == S06) {
+ n1 = Integer.parseInt(s.substring(mark).trim());
+ l.add(new NumberRange(eq, n1, isNot));
+ } else /* (state == S11) */ {
+ n2 = Integer.parseInt(s.substring(mark).trim());
+ l.add(new NumberRange(eq, n1, n2, isNot));
+ }
+
+ numberRanges = l.toArray(new NumberRange[l.size()]);
+ }
+
+ @Override /* Matcher */
+ public boolean matches(ClassMeta<?> cm, Object o) {
+ Number n = (Number)o;
+ if (numberRanges.length == 0) return true;
+ for (int i = 0; i < numberRanges.length; i++)
+ if (numberRanges[i].matches(n))
+ return true;
+ return false;
+ }
+ }
+
+ /**
+ * A construct representing a single search range in a single search pattern.
+ * All possible forms of search patterns are boiled down to these number ranges.
+ */
+ private static class NumberRange {
+ int start;
+ int end;
+ boolean isNot;
+
+ public NumberRange(Equality eq, Integer num, boolean isNot) {
+ this(eq, num, null, isNot);
+ }
+
+ public NumberRange(Equality eq, Integer start, Integer end, boolean isNot) {
+ this.isNot = isNot;
+
+ // 123, >123, <123, >=123, <=123, >-123, >=-123, 123-456, -123--456
+ if (eq == Equality.NONE && end == null) { // 123
+ this.start = start;
+ this.end = this.start;
+ } else if (eq == Equality.GT) {
+ this.start = start+1;
+ this.end = Integer.MAX_VALUE;
+ } else if (eq == Equality.GTE) {
+ this.start = start;
+ this.end = Integer.MAX_VALUE;
+ } else if (eq == Equality.LT) {
+ this.start = Integer.MIN_VALUE;
+ this.end = start-1;
+ } else if (eq == Equality.LTE) {
+ this.start = Integer.MIN_VALUE;
+ this.end = start;
+ } else {
+ this.start = start;
+ this.end = end;
+ }
+ }
+
+ public boolean matches(Number n) {
+ long i = n.longValue();
+ boolean b = (i>=start && i<=end);
+ if (isNot) b = !b;
+ return b;
+ }
+ }
+}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PatternException.java
similarity index 67%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PatternException.java
index 5231a47..e6bfb4e 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PatternException.java
@@ -10,33 +10,24 @@
// * "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.rest.widget;
+package org.apache.juneau.pojotools;
import org.apache.juneau.*;
-import org.apache.juneau.rest.*;
/**
- * Widget that places a powered-by-Juneau message on the page.
- *
- * <p>
- * The variable it resolves is <js>"$W{PoweredByJuneau}"</js>.
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- * <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.PredefinedWidgets}
- * </ul>
+ * Indicates an invalid search pattern was specified.
*/
-public class PoweredByJuneau extends Widget {
+public class PatternException extends FormattedRuntimeException {
+ private static final long serialVersionUID = 1L;
/**
- * Returns an Apache Juneau image tag hyperlinked to <js>"http://juneau.apache.org"</js>
+ * Constructor.
+ *
+ * @param message Message.
+ * @param args Message arguments.
*/
- @Override /* Widget */
- public String getHtml(RestRequest req) throws Exception {
- UriResolver r = req.getUriResolver();
- return "<a href='http://juneau.apache.org'><img style='float:right;padding-right:20px;height:32px' src='"+r.resolve("servlet:/htdocs/images/juneau.png")+"'>";
+ public PatternException(String message, Object...args) {
+ super(message, args);
}
}
-
-
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoPaginator.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoPaginator.java
new file mode 100644
index 0000000..465bcd8
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoPaginator.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.pojotools;
+
+import org.apache.juneau.*;
+
+/**
+ * Designed to provide paging on POJOs consisting of arrays and collections.
+ *
+ * <p>
+ * Allows you to quickly return subsets of arrays and collections based on position/limit arguments.
+ */
+public final class PojoPaginator implements PojoTool<Object> {
+
+ @Override /* PojoTool */
+ public Object run(BeanSession session, Object input, Object args) {
+
+// if (input == null)
+// return null;
+//
+// ClassMeta type = session.getClassMetaForObject(input);
+//
+// if (! type.isCollectionOrArray())
+// return input;
+//
+// int pos = args.getPosition();
+// int limit = args.getLimit();
+//
+// if (type.isArray()) {
+// int size = Array.getLength(input);
+// int end = (limit+pos >= size) ? size : limit + pos;
+// pos = Math.min(pos, size);
+// ClassMeta<?> et = type.getElementType();
+// if (! et.isPrimitive())
+// return copyOfRange((Object[])input, pos, end);
+// if (et.isType(boolean.class))
+// return copyOfRange((boolean[])input, pos, end);
+// if (et.isType(byte.class))
+// return copyOfRange((byte[])input, pos, end);
+// if (et.isType(char.class))
+// return copyOfRange((char[])input, pos, end);
+// if (et.isType(double.class))
+// return copyOfRange((double[])input, pos, end);
+// if (et.isType(float.class))
+// return copyOfRange((float[])input, pos, end);
+// if (et.isType(int.class))
+// return copyOfRange((int[])input, pos, end);
+// if (et.isType(long.class))
+// return copyOfRange((long[])input, pos, end);
+// if (et.isType(short.class))
+// return copyOfRange((short[])input, pos, end);
+// return null;
+// }
+//
+// List l = type.isList() ? (List)input : new ArrayList((Collection)input);
+// int end = (limit+pos >= l.size()) ? l.size() : limit + pos;
+// pos = Math.min(pos, l.size());
+// return l.subList(pos, end);
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoSearcher.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoSearcher.java
new file mode 100644
index 0000000..1abeffe
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoSearcher.java
@@ -0,0 +1,185 @@
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+
+/**
+ * Designed to provide paging on POJOs consisting of arrays and collections.
+ *
+ * <p>
+ * Allows you to quickly return subsets of arrays and collections based on position/limit arguments.
+ */
+@SuppressWarnings({"rawtypes"})
+public final class PojoSearcher implements PojoTool<SearchArgs> {
+
+ /**
+ * Default reusable searcher.
+ */
+ public static final PojoSearcher DEFAULT = new PojoSearcher();
+
+ final MatcherFactory[] factories;
+
+ /**
+ * TODO
+ *
+ * @param factories
+ */
+ public PojoSearcher(MatcherFactory...factories) {
+ this.factories = factories;
+ }
+
+ /**
+ * TODO
+ *
+ */
+ public PojoSearcher() {
+ this(NumberMatcherFactory.DEFAULT, TimeMatcherFactory.DEFAULT, StringMatcherFactory.DEFAULT);
+ }
+
+ @Override /* PojoTool */
+ public Object run(BeanSession session, Object input, SearchArgs args) {
+
+ ClassMeta<?> type = session.getClassMetaForObject(input);
+ Map<String,String> search = args.getSearch();
+
+ if (search.isEmpty() || type == null || ! type.isCollectionOrArray())
+ return input;
+
+ List<Object> l = null;
+ RowMatcher rowMatcher = new RowMatcher(session, search);
+
+ if (type.isCollection()) {
+ Collection c = (Collection)input;
+ l = new ArrayList<>(c.size());
+ for (Object o : c) {
+ if (rowMatcher.matches(o))
+ l.add(o);
+ }
+
+ } else /* isArray */ {
+ int size = Array.getLength(input);
+ l = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ Object o = Array.get(input, i);
+ if (rowMatcher.matches(o))
+ l.add(o);
+ }
+ }
+
+ return l;
+ }
+
+ //====================================================================================================
+ // MapMatcher
+ //====================================================================================================
+ /*
+ * Matches on a Map only if all specified entry matchers match.
+ */
+ private class RowMatcher {
+
+ Map<String,ColumnMatcher> entryMatchers = new HashMap<>();
+ BeanSession bs;
+
+ RowMatcher(BeanSession bs, Map query) {
+ this.bs = bs;
+ for (Map.Entry e : (Set<Map.Entry>)query.entrySet())
+ entryMatchers.put(asString(e.getKey()), new ColumnMatcher(bs, asString(e.getValue())));
+ }
+
+ boolean matches(Object o) {
+ if (o == null)
+ return false;
+ ClassMeta<?> cm = bs.getClassMetaForObject(o);
+ if (cm.isMapOrBean()) {
+ Map m = cm.isMap() ? (Map)o : bs.toBeanMap(o);
+ for (Map.Entry<String,ColumnMatcher> e : entryMatchers.entrySet()) {
+ String key = e.getKey();
+ Object val = null;
+ if (m instanceof BeanMap) {
+ val = ((BeanMap)m).getRaw(key);
+ } else {
+ val = m.get(key);
+ }
+ if (! e.getValue().matches(val))
+ return false;
+ }
+ return true;
+ }
+ if (cm.isCollection()) {
+ for (Object o2 : (Collection)o)
+ if (! matches(o2))
+ return false;
+ return true;
+ }
+ if (cm.isArray()) {
+ for (int i = 0; i < Array.getLength(o); i++)
+ if (! matches(Array.get(o, i)))
+ return false;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ //====================================================================================================
+ // ObjectMatcher
+ //====================================================================================================
+ /*
+ * Matcher that uses the correct matcher based on object type.
+ * Used for objects when we can't determine the object type beforehand.
+ */
+ private class ColumnMatcher {
+
+ String searchPattern;
+ Matcher[] matchers;
+ BeanSession bs;
+
+ ColumnMatcher(BeanSession bs, String searchPattern) {
+ this.bs = bs;
+ this.searchPattern = searchPattern;
+ this.matchers = new Matcher[factories.length];
+ }
+
+ boolean matches(Object o) {
+ ClassMeta<?> cm = bs.getClassMetaForObject(o);
+ if (cm == null)
+ return false;
+ if (cm.isCollection()) {
+ for (Object o2 : (Collection)o)
+ if (matches(o2))
+ return true;
+ return false;
+ }
+ if (cm.isArray()) {
+ for (int i = 0; i < Array.getLength(o); i++)
+ if (matches(Array.get(o, i)))
+ return true;
+ return false;
+ }
+ for (int i = 0; i < factories.length; i++) {
+ if (factories[i].canMatch(cm)) {
+ if (matchers[i] == null)
+ matchers[i] = factories[i].create(searchPattern);
+ return matchers[i].matches(cm, o);
+ }
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoSorter.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoSorter.java
new file mode 100644
index 0000000..67ac4e6
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoSorter.java
@@ -0,0 +1,110 @@
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Sorts arrays and collections of maps and beans.
+ */
+@SuppressWarnings({"unchecked","rawtypes"})
+public final class PojoSorter implements PojoTool<SortArgs> {
+
+ @Override /* PojoTool */
+ public Object run(BeanSession session, Object input, SortArgs args) {
+ if (input == null)
+ return null;
+
+ // If sort or view isn't empty, then we need to make sure that all entries in the
+ // list are maps.
+ Map<String,Boolean> sort = args.getSort();
+
+ if (sort.isEmpty())
+ return input;
+
+ ClassMeta type = session.getClassMetaForObject(input);
+
+ if (! type.isCollectionOrArray())
+ return input;
+
+ ArrayList<SortEntry> l = null;
+
+ if (type.isArray()) {
+ int size = Array.getLength(input);
+ l = new ArrayList<>(size);
+ for (int i = 0; i < size; i++)
+ l.add(new SortEntry(session, Array.get(input, i)));
+ } else /* isCollection() */ {
+ Collection c = (Collection)input;
+ l = new ArrayList<>(c.size());
+ for (Object o : c)
+ l.add(new SortEntry(session, o));
+ }
+
+ // We reverse the list and sort last to first.
+ List<String> columns = new ArrayList<>(sort.keySet());
+ Collections.reverse(columns);
+
+ for (final String c : columns) {
+ final boolean isDesc = sort.get(c);
+ for (SortEntry se : l)
+ se.setSort(c, isDesc);
+ Collections.sort(l);
+ }
+
+ ArrayList<Object> l2 = new ArrayList<>(l.size());
+ for (SortEntry se : l)
+ l2.add(se.o);
+
+ return l2;
+ }
+
+ private static class SortEntry implements Comparable {
+ Object o;
+ ClassMeta<?> cm;
+ BeanSession bs;
+
+ Object sortVal;
+ boolean isDesc;
+
+ SortEntry(BeanSession bs, Object o) {
+ this.o = o;
+ this.bs = bs;
+ this.cm = bs.getClassMetaForObject(o);
+ }
+
+ void setSort(String sortCol, boolean isDesc) {
+ this.isDesc = isDesc;
+
+ if (cm == null)
+ sortVal = null;
+ else if (cm.isMap())
+ sortVal = ((Map)o).get(sortCol);
+ else if (cm.isBean())
+ sortVal = bs.toBeanMap(o).get(sortCol);
+ else
+ sortVal = null;
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ if (isDesc)
+ return ObjectUtils.compare(((SortEntry)o).sortVal, this.sortVal);
+ return ObjectUtils.compare(this.sortVal, ((SortEntry)o).sortVal);
+ }
+ }
+}
\ No newline at end of file
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoTool.java
similarity index 67%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoTool.java
index 5231a47..ead70f8 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoTool.java
@@ -10,33 +10,24 @@
// * "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.rest.widget;
+package org.apache.juneau.pojotools;
import org.apache.juneau.*;
-import org.apache.juneau.rest.*;
/**
- * Widget that places a powered-by-Juneau message on the page.
+ * Interface for classes that convert POJOs in some way using some predefined arguments object.
*
- * <p>
- * The variable it resolves is <js>"$W{PoweredByJuneau}"</js>.
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- * <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.PredefinedWidgets}
- * </ul>
+ * @param <T> The argument object type.
*/
-public class PoweredByJuneau extends Widget {
-
+public interface PojoTool<T> {
/**
- * Returns an Apache Juneau image tag hyperlinked to <js>"http://juneau.apache.org"</js>
+ * Converts the specified input to some other output.
+ *
+ * @param session The current bean session.
+ * @param input The input POJO.
+ * @param args The arguments.
+ * @return The output POJO.
*/
- @Override /* Widget */
- public String getHtml(RestRequest req) throws Exception {
- UriResolver r = req.getUriResolver();
- return "<a href='http://juneau.apache.org'><img style='float:right;padding-right:20px;height:32px' src='"+r.resolve("servlet:/htdocs/images/juneau.png")+"'>";
- }
+ public Object run(BeanSession session, Object input, T args);
}
-
-
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoViewer.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoViewer.java
new file mode 100644
index 0000000..b2bd530
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PojoViewer.java
@@ -0,0 +1,80 @@
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * Designed to provide paging on POJOs consisting of arrays and collections.
+ *
+ * <p>
+ * Allows you to quickly return subsets of arrays and collections based on position/limit arguments.
+ */
+@SuppressWarnings({"unchecked","rawtypes"})
+public final class PojoViewer implements PojoTool<ViewArgs> {
+
+ @Override /* PojoTool */
+ public Object run(BeanSession session, Object input, ViewArgs args) {
+
+ if (input == null)
+ return null;
+
+ List<String> view = args.getView();
+ ClassMeta type = session.getClassMetaForObject(input);
+
+ if (type.isBeanMap())
+ return new DelegateBeanMap(((BeanMap)input).getBean(), session).filterKeys(view);
+ if (type.isMap())
+ return new DelegateMap((Map)input, session).filterKeys(view);
+ if (type.isBean())
+ return new DelegateBeanMap(input, session).filterKeys(view);
+
+ ArrayList<Object> l = null;
+
+ if (type.isArray()) {
+ int size = Array.getLength(input);
+ l = new ArrayList<>(size);
+ for (int i = 0; i < size; i++)
+ l.add(Array.get(input, i));
+ } else if (type.isCollection()) {
+ Collection c = (Collection)input;
+ l = new ArrayList<>(c.size());
+ for (Object o : c)
+ l.add(o);
+ } else {
+ return input;
+ }
+
+ for (ListIterator li = l.listIterator(); li.hasNext();) {
+ Object o = li.next();
+ ClassMeta cm2 = session.getClassMetaForObject(o);
+
+ if (cm2 == null)
+ o = null;
+ else if (cm2.isBeanMap())
+ o = new DelegateBeanMap(((BeanMap)o).getBean(), session).filterKeys(view);
+ else if (cm2.isMap())
+ o = new DelegateMap((Map)o, session).filterKeys(view);
+ else if (cm2.isBean())
+ o = new DelegateBeanMap(o, session).filterKeys(view);
+
+ li.set(o);
+ }
+
+ return l;
+ }
+}
\ No newline at end of file
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/SearchArgs.java
similarity index 55%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/SearchArgs.java
index 4d8c46d..54fc752 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/SearchArgs.java
@@ -10,69 +10,68 @@
// * "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.utils;
+package org.apache.juneau.pojotools;
import java.util.*;
+import org.apache.juneau.internal.*;
+
/**
- * An extension of {@link LinkedHashSet} with a convenience {@link #append(Object)} method.
- *
- * <p>
- * Primarily used for testing purposes for quickly creating populated sets.
- * <p class='bcode w800'>
- * <jc>// Example:</jc>
- * Set<String> s = <jk>new</jk> ASet<String>().append(<js>"foo"</js>).append(<js>"bar"</js>);
- * </p>
- *
- * @param <T> The entry type.
+ * Encapsulates arguments for the {@link PojoSorter} class.
*/
-@SuppressWarnings({"unchecked"})
-public final class ASet<T> extends LinkedHashSet<T> {
+public class SearchArgs {
+
+ private final Map<String,String> search = new LinkedHashMap<>();
- private static final long serialVersionUID = 1L;
/**
- * Convenience method for creating a list of objects.
+ * Constructor.
*
- * @param t The initial values.
- * @return A new list.
+ * @param searchArgs Search arguments.
*/
- public static <T> ASet<T> create(T...t) {
- return new ASet<T>().appendAll(t);
+ public SearchArgs(String searchArgs) {
+ this(Arrays.asList(StringUtils.split(searchArgs, ',')));
}
/**
- * Adds an entry to this set.
+ * Constructor.
*
- * @param t The entry to add to this set.
- * @return This object (for method chaining).
+ * @param searchArgs Search arguments.
*/
- public ASet<T> append(T t) {
- add(t);
- return this;
+ public SearchArgs(List<String> searchArgs) {
+ for (String s : searchArgs) {
+ int i = StringUtils.indexOf(s, '=', '>', '<');
+ if (i == -1)
+ throw new PatternException("Invalid search terms: ''{0}''", searchArgs);
+ char c = s.charAt(i);
+ append(s.substring(0, i).trim(), s.substring(c == '=' ? i+1 : i).trim());
+ }
}
/**
- * Adds multiple entries to this set.
+ * Appends the specified search argument.
*
- * @param t The entries to add to this set.
+ * @param column The column name to search.
+ * @param searchTerm The search term.
* @return This object (for method chaining).
*/
- public ASet<T> appendAll(T...t) {
- addAll(Arrays.asList(t));
+ public SearchArgs append(String column, String searchTerm) {
+ this.search.put(column, searchTerm);
return this;
}
/**
- * Adds a value to this set if the boolean value is <jk>true</jk>
+ * The query search terms.
*
- * @param b The boolean value.
- * @param t The value to add.
- * @return This object (for method chaining).
+ * <p>
+ * The search terms are key/value pairs consisting of column-names and search tokens.
+ *
+ * <p>
+ * It's up to implementers to decide the syntax and meaning of the search term.
+ *
+ * @return An unmodifiable map of query search terms.
*/
- public ASet<T> appendIf(boolean b, T t) {
- if (b)
- append(t);
- return this;
+ public Map<String,String> getSearch() {
+ return search;
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/SortArgs.java
similarity index 51%
copy from juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/SortArgs.java
index 83d7440..45d9652 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DelegateMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/SortArgs.java
@@ -1,58 +1,80 @@
-// ***************************************************************************************************************************
-// * 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;
-
-import java.util.*;
-
-import org.apache.juneau.*;
-
-/**
- * Represents a wrapped {@link Map} where entries in the map can be removed without affecting the underlying map.
- *
- * @param <T> The class type of the wrapped bean.
- */
-public class DelegateMap<T> extends ObjectMap implements Delegate<T> {
- private static final long serialVersionUID = 1L;
-
- private transient ClassMeta<T> classMeta;
-
- /**
- * Constructor.
- *
- * @param classMeta The metadata object that created this delegate object.
- */
- public DelegateMap(ClassMeta<T> classMeta) {
- this.classMeta = classMeta;
- }
-
- @Override /* Delegate */
- public ClassMeta<T> getClassMeta() {
- return classMeta;
- }
-
- /**
- * Remove all but the specified keys from this map.
- *
- * <p>
- * This does not affect the underlying map.
- *
- * @param keys The remaining keys in the map (in the specified order).
- */
- public void filterKeys(List<String> keys) {
- ObjectMap m2 = new ObjectMap();
- for (String k : keys)
- m2.put(k, get(k));
- this.clear();
- this.putAll(m2);
- }
-}
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import static java.util.Collections.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.util.*;
+
+/**
+ * Encapsulates arguments for the {@link PojoSorter} class.
+ */
+public class SortArgs {
+
+ private final Map<String,Boolean> sort;
+
+ /**
+ * Constructor.
+ *
+ * @param sortArgs
+ * Sort arguments.
+ * <br>Values are of the following forms:
+ * <ul>
+ * <li><js>"column"</js> - Sort column ascending.
+ * <li><js>"column+"</js> - Sort column ascending.
+ * <li><js>"column-"</js> - Sort column descending.
+ * </ul>
+ */
+ public SortArgs(String...sortArgs) {
+ this(Arrays.asList(sortArgs));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param sortArgs
+ * Sort arguments.
+ * <br>Values are of the following forms:
+ * <ul>
+ * <li><js>"column"</js> - Sort column ascending.
+ * <li><js>"column+"</js> - Sort column ascending.
+ * <li><js>"column-"</js> - Sort column descending.
+ * </ul>
+ */
+ public SortArgs(Collection<String> sortArgs) {
+ Map<String,Boolean> sort = new LinkedHashMap<>();
+ for (String s : sortArgs) {
+ boolean isDesc = false;
+ if (endsWith(s, '-', '+')) {
+ isDesc = endsWith(s, '-');
+ s = s.substring(0, s.length()-1);
+ }
+ sort.put(s, isDesc);
+ }
+ this.sort = unmodifiableMap(sort);
+ }
+
+ /**
+ * The sort columns.
+ *
+ * <p>
+ * The sort columns are key/value pairs consisting of column-names and direction flags
+ * (<jk>false</jk> = ascending, <jk>true</jk> = descending).
+ *
+ * @return An unmodifiable ordered map of sort columns and directions.
+ */
+ public Map<String,Boolean> getSort() {
+ return sort;
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/StringMatcherFactory.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/StringMatcherFactory.java
new file mode 100644
index 0000000..2e63d6f
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/StringMatcherFactory.java
@@ -0,0 +1,145 @@
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.util.*;
+import java.util.regex.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * TODO
+ *
+ */
+public class StringMatcherFactory extends MatcherFactory {
+
+ /**
+ * Default reusable matcher.
+ */
+ public static final StringMatcherFactory DEFAULT = new StringMatcherFactory();
+
+ @Override
+ public boolean canMatch(ClassMeta<?> cm) {
+ return true;
+ }
+
+ @Override
+ public Matcher create(String pattern) {
+ return new StringMatcher(pattern);
+ }
+
+ /**
+ * A construct representing a single search pattern.
+ */
+ private static class StringMatcher extends Matcher {
+ private static final AsciiSet
+ META_CHARS = AsciiSet.create("*?'\""),
+ SQ_CHAR = AsciiSet.create("'"),
+ DQ_CHAR = AsciiSet.create("\""),
+ REGEX_CHARS = AsciiSet.create("+\\[]{}()^$.");
+
+ Pattern[] orPatterns, andPatterns, notPatterns;
+
+ public StringMatcher(String searchPattern) {
+
+ List<Pattern> ors = new LinkedList<>();
+ List<Pattern> ands = new LinkedList<>();
+ List<Pattern> nots = new LinkedList<>();
+
+ for (String s : splitQuoted(searchPattern, true)) {
+ char c0 = s.charAt(0), c9 = s.charAt(s.length()-1);
+
+ if (c0 == '/' && c9 == '/' && s.length() > 1) {
+ ands.add(Pattern.compile(strip(s)));
+ } else {
+ char prefix = '^';
+ boolean ignoreCase = false;
+ if (s.length() > 1 && (c0 == '^' || c0 == '+' || c0 == '-')) {
+ prefix = c0;
+ s = s.substring(1);
+ c0 = s.charAt(0);
+ }
+
+ if (c0 == '\'') {
+ s = unEscapeChars(strip(s), SQ_CHAR);
+ ignoreCase = true;
+ } else if (c0 == '"') {
+ s = unEscapeChars(strip(s), DQ_CHAR);
+ }
+
+ if (REGEX_CHARS.contains(s) || META_CHARS.contains(s)) {
+ StringBuilder sb = new StringBuilder();
+ boolean isInEscape = false;
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (isInEscape) {
+ if (c == '?' || c == '*' || c == '\\')
+ sb.append('\\').append(c);
+ else
+ sb.append(c);
+ isInEscape = false;
+ } else {
+ if (c == '\\')
+ isInEscape = true;
+ else if (c == '?')
+ sb.append(".?");
+ else if (c == '*')
+ sb.append(".*");
+ else if (REGEX_CHARS.contains(c))
+ sb.append("\\").append(c);
+ else
+ sb.append(c);
+ }
+ }
+ s = sb.toString();
+ }
+
+
+ int flags = Pattern.DOTALL;
+ if (ignoreCase)
+ flags |= Pattern.CASE_INSENSITIVE;
+
+ Pattern p = Pattern.compile(s, flags);
+
+ if (prefix == '-')
+ nots.add(p);
+ else if (prefix == '+')
+ ands.add(p);
+ else
+ ors.add(p);
+ }
+ }
+ orPatterns = ors.toArray(new Pattern[ors.size()]);
+ andPatterns = ands.toArray(new Pattern[ands.size()]);
+ notPatterns = nots.toArray(new Pattern[nots.size()]);
+ }
+
+ @Override
+ public boolean matches(ClassMeta<?> cm, Object o) {
+ String s = (String)o;
+ for (int i = 0; i < andPatterns.length; i++)
+ if (! andPatterns[i].matcher(s).matches())
+ return false;
+ for (int i = 0; i < notPatterns.length; i++)
+ if (notPatterns[i].matcher(s).matches())
+ return false;
+ for (int i = 0; i < orPatterns.length; i++)
+ if (orPatterns[i].matcher(s).matches())
+ return true;
+ return orPatterns.length == 0;
+ }
+ }
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/TimeMatcherFactory.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/TimeMatcherFactory.java
new file mode 100644
index 0000000..b27ea5b
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/TimeMatcherFactory.java
@@ -0,0 +1,463 @@
+// ***************************************************************************************************************************
+// * 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.pojotools;
+
+import static java.util.Calendar.*;
+import static org.apache.juneau.internal.StateMachineState.*;
+
+import java.text.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * TODO
+ */
+public class TimeMatcherFactory extends MatcherFactory {
+
+ /**
+ * Default reusable matcher.
+ */
+ public static final TimeMatcherFactory DEFAULT = new TimeMatcherFactory();
+
+ private final SimpleDateFormat[] formats;
+
+ /**
+ * Constructor.
+ */
+ protected TimeMatcherFactory() {
+ this.formats = getTimestampFormats();
+ }
+
+ /**
+ * TODO
+ *
+ * @return TODO
+ */
+ protected SimpleDateFormat[] getTimestampFormats() {
+ String[] s = getTimestampFormatStrings();
+ SimpleDateFormat[] a = new SimpleDateFormat[s.length];
+ for (int i = 0; i < s.length; i++)
+ a[i] = new SimpleDateFormat(s[i]);
+ return a;
+ }
+
+ /**
+ * TODO
+ *
+ * @return TODO
+ */
+ protected String[] getTimestampFormatStrings() {
+ return new String[]{
+ "yyyy-MM-dd'T'HH:mm:ss",
+ "yyyy-MM-dd'T'HH:mm",
+ "yyyy-MM-dd'T'HH",
+ "yyyy-MM-dd",
+ "yyyy-MM",
+ "yyyy"
+ };
+ }
+
+ @Override
+ public boolean canMatch(ClassMeta<?> cm) {
+ return cm.isDateOrCalendar();
+ }
+
+ @Override
+ public Matcher create(String pattern) {
+ return new TimeMatcher(formats, pattern);
+ }
+
+ /**
+ * A construct representing a single search pattern.
+ */
+ private static class TimeMatcher extends Matcher {
+
+ private static final AsciiSet
+ DT = AsciiSet.create("0123456789-:T./"),
+ WS = AsciiSet.create(" \t");
+
+ TimestampRange[] ranges;
+ List<TimestampRange> l = new LinkedList<>();
+
+ public TimeMatcher(SimpleDateFormat[] f, String s) {
+
+ // Possible patterns:
+ // >2000, <2000, >=2000, <=2000, > 2000, 2000 - 2001, '2000', >'2000', '2000'-'2001', '2000' - '2001'
+
+ // Possible states:
+ // S01 = Looking for [<]/[>]/quote/NUM ([>]=S02, [<]=S03, [']=S05, ["]=S06, NUM=S08)
+ // S02 = Found [>], looking for [=]/quote/NUM ([=]=S04, [']=S05, ["]=S06, NUM=S08)
+ // S03 = Found [<], looking for [=]/quote/NUM ([=]=S04, [']=S05, ["]=S06, NUM=S08)
+ // S04 = Found [>=] or [<=], looking for quote/NUM ([']=S05, ["]=S06, NUM=S08)
+ // S05 = Found ['], looking for ['] ([']=S01)
+ // S06 = Found ["], looking for ["] (["]=S01)
+ // S07 = Found [123"] or [123'], looking for WS (WS=S09)
+ // S08 = Found [2], looking for WS (WS=S09)
+ // S09 = Found [2000 ], looking for [-]/quote/NUM ([-]=S10, [']=S11, ["]=S12, NUM=S13)
+ // S10 = Found [2000 -], looking for quote/NUM ([']=S11, ["]=S12, NUM=S13)
+ // S11 = Found [2000 - '], looking for ['] ([']=S01)
+ // S12 = Found [2000 - "], looking for ["] (["]=S01)
+ // S13 = Found [2000 - 2], looking for WS (WS=S01)
+
+ StateMachineState state = S01;
+ int mark = 0;
+ Equality eq = Equality.NONE;
+ String s1 = null, s2 = null;
+
+ int i;
+ char c = 0;
+ for (i = 0; i < s.trim().length(); i++) {
+ c = s.charAt(i);
+ if (state == S01) {
+ // S01 = Looking for [>]/[<]/quote/NUM ([>]=S02, [<]=S03, [']=S05, ["]=S06, NUM=S08)
+ if (WS.contains(c)) {
+ state = S01;
+ } else if (c == '>') {
+ state = S02;
+ eq = Equality.GT;
+ } else if (c == '<') {
+ state = S03;
+ eq = Equality.LT;
+ } else if (c == '\'') {
+ state = S05;
+ mark = i+1;
+ } else if (c == '"') {
+ state = S06;
+ mark = i+1;
+ } else if (DT.contains(c)) {
+ state = S08;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S02) {
+ // S02 = Found [>], looking for [=]/quote/NUM ([=]=S04, [']=S05, ["]=S06, NUM=S08)
+ if (WS.contains(c)) {
+ state = S02;
+ } else if (c == '=') {
+ state = S04;
+ eq = Equality.GTE;
+ } else if (c == '\'') {
+ state = S05;
+ mark = i+1;
+ } else if (c == '"') {
+ state = S06;
+ mark = i+1;
+ } else if (DT.contains(c)) {
+ state = S08;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S03) {
+ // S03 = Found [<], looking for [=]/quote/NUM ([=]=S04, [']=S05, ["]=S06, NUM=S08)
+ if (WS.contains(c)) {
+ state = S03;
+ } else if (c == '=') {
+ state = S04;
+ eq = Equality.LTE;
+ } else if (c == '\'') {
+ state = S05;
+ mark = i+1;
+ } else if (c == '"') {
+ state = S06;
+ mark = i+1;
+ } else if (DT.contains(c)) {
+ state = S08;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S04) {
+ // S04 = Found [>=] or [<=], looking for quote/NUM ([']=S05, ["]=S06, NUM=S08)
+ if (WS.contains(c)) {
+ state = S04;
+ } else if (c == '\'') {
+ state = S05;
+ mark = i+1;
+ } else if (c == '"') {
+ state = S06;
+ mark = i+1;
+ } else if (DT.contains(c)) {
+ state = S08;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S05) {
+ // S05 = Found ['], looking for ['] ([']=S07)
+ if (c == '\'') {
+ state = S07;
+ s1 = s.substring(mark, i);
+ }
+ } else if (state == S06) {
+ // S06 = Found ["], looking for ["] (["]=S07)
+ if (c == '"') {
+ state = S07;
+ s1 = s.substring(mark, i);
+ }
+ } else if (state == S07) {
+ // S07 = Found [123"] or [123'], looking for WS (WS=S09)
+ if (WS.contains(c)) {
+ state = S09;
+ } else if (c == '-') {
+ state = S10;
+ } else {
+ break;
+ }
+ } else if (state == S08) {
+ // S08 = Found [1], looking for WS (WS=S09)
+ if (WS.contains(c)) {
+ state = S09;
+ s1 = s.substring(mark, i);
+ }
+ } else if (state == S09) {
+ // S09 = Found [2000 ], looking for [-]/[>]/[<]/quote/NUM ([-]=S10, [>]=S02, [<]=S03, [']=S05, ["]=S06, NUM=S08)
+ if (WS.contains(c)) {
+ state = S09;
+ } else if (c == '-') {
+ state = S10;
+ } else if (c == '>') {
+ state = S02;
+ l.add(new TimestampRange(f, eq, s1));
+ eq = Equality.GT;
+ s1 = null;
+ } else if (c == '<') {
+ state = S03;
+ l.add(new TimestampRange(f, eq, s1));
+ eq = Equality.LT;
+ s1 = null;
+ } else if (c == '\'') {
+ state = S05;
+ l.add(new TimestampRange(f, eq, s1));
+ mark = i+1;
+ eq = null;
+ s1 = null;
+ } else if (c == '"') {
+ state = S06;
+ l.add(new TimestampRange(f, eq, s1));
+ mark = i+1;
+ eq = null;
+ s1 = null;
+ } else if (DT.contains(c)) {
+ state = S08;
+ l.add(new TimestampRange(f, eq, s1));
+ eq = null;
+ s1 = null;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S10) {
+ // S10 = Found [2000 -], looking for quote/NUM ([']=S11, ["]=S12, NUM=S13)
+ if (WS.contains(c)) {
+ state = S10;
+ } else if (c == '\'') {
+ state = S11;
+ mark = i+1;
+ } else if (c == '"') {
+ state = S12;
+ mark = i+1;
+ } else if (DT.contains(c)) {
+ state = S13;
+ mark = i;
+ } else {
+ break;
+ }
+ } else if (state == S11) {
+ // S11 = Found [2000 - '], looking for ['] ([']=S01)
+ if (c == '\'') {
+ state = S01;
+ s2 = s.substring(mark, i);
+ l.add(new TimestampRange(f, s1, s2));
+ s1 = null;
+ s2 = null;
+ }
+ } else if (state == S12) {
+ // S12 = Found [2000 - "], looking for ["] (["]=S01)
+ if (c == '"') {
+ state = S01;
+ s2 = s.substring(mark, i);
+ l.add(new TimestampRange(f, s1, s2));
+ s1 = null;
+ s2 = null;
+ }
+ } else /* (state == S13) */ {
+ // S13 = Found [2000 - 2], looking for WS (WS=S01)
+ if (WS.contains(c)) {
+ state = S01;
+ s2 = s.substring(mark, i);
+ l.add(new TimestampRange(f, s1, s2));
+ s1 = null;
+ s2 = null;
+ }
+ }
+ }
+
+ if (i != s.length())
+ throw new PatternException("Invalid range pattern ({0}): pattern=[{1}], pos=[{2}], char=[{3}]", state, s, i, c);
+
+ if (state == S01) {
+ // No tokens found.
+ } else if (state == S02 || state == S03 || state == S04 || state == S05 || state == S06 || state == S10 || state == S11 || state == S12) {
+ System.err.println("state=["+state+"]");
+ throw new PatternException("Invalid range pattern (E{0}): {1}", state, s);
+ } else if (state == S07) {
+ l.add(new TimestampRange(f, eq, s1));
+ } else if (state == S08) {
+ s1 = s.substring(mark).trim();
+ l.add(new TimestampRange(f, eq, s1));
+ } else /* (state == S13) */ {
+ s2 = s.substring(mark).trim();
+ l.add(new TimestampRange(f, s1, s2));
+ }
+
+ ranges = l.toArray(new TimestampRange[l.size()]);
+ }
+
+ @Override
+ public boolean matches(ClassMeta<?> cm, Object o) {
+ if (ranges.length == 0) return true;
+
+ Calendar c = null;
+ if (cm.isCalendar())
+ c = (Calendar)o;
+ else {
+ c = Calendar.getInstance();
+ c.setTime((Date)o);
+ }
+ for (int i = 0; i < ranges.length; i++)
+ if (ranges[i].matches(c))
+ return true;
+ return false;
+ }
+ }
+
+ /**
+ * A construct representing a single search range in a single search pattern.
+ * All possible forms of search patterns are boiled down to these timestamp ranges.
+ */
+ private static class TimestampRange {
+ Calendar start;
+ Calendar end;
+
+ public TimestampRange(SimpleDateFormat[] formats, String start, String end) {
+ CalendarP start1 = parseDate(formats, start);
+ CalendarP end1 = parseDate(formats, end);
+ this.start = start1.copy().roll(MILLISECOND, -1).getCalendar();
+ this.end = end1.roll(1).getCalendar();
+ }
+
+ public TimestampRange(SimpleDateFormat[] formats, Equality eq, String singleDate) {
+ System.err.println("eq=["+eq+"], singleDate=["+singleDate+"]");
+
+ CalendarP singleDate1 = parseDate(formats, singleDate);
+ if (eq == Equality.GT) {
+ this.start = singleDate1.roll(1).roll(MILLISECOND, -1).getCalendar();
+ this.end = new CalendarP(new Date(Long.MAX_VALUE), 0).getCalendar();
+ } else if (eq == Equality.LT) {
+ this.start = new CalendarP(new Date(0), 0).getCalendar();
+ this.end = singleDate1.getCalendar();
+ } else if (eq == Equality.GTE) {
+ this.start = singleDate1.roll(MILLISECOND, -1).getCalendar();
+ this.end = new CalendarP(new Date(Long.MAX_VALUE), 0).getCalendar();
+ } else if (eq == Equality.LTE) {
+ this.start = new CalendarP(new Date(0), 0).getCalendar();
+ this.end = singleDate1.roll(1).getCalendar();
+ } else {
+ this.start = singleDate1.copy().roll(MILLISECOND, -1).getCalendar();
+ this.end = singleDate1.roll(1).getCalendar();
+ }
+ }
+
+ public boolean matches(Calendar c) {
+ boolean b = (c.after(start) && c.before(end));
+ return b;
+ }
+ }
+
+ private static int getPrecisionField(String pattern) {
+ if (pattern.indexOf('s') != -1)
+ return SECOND;
+ if (pattern.indexOf('m') != -1)
+ return MINUTE;
+ if (pattern.indexOf('H') != -1)
+ return HOUR_OF_DAY;
+ if (pattern.indexOf('d') != -1)
+ return DAY_OF_MONTH;
+ if (pattern.indexOf('M') != -1)
+ return MONTH;
+ if (pattern.indexOf('y') != -1)
+ return YEAR;
+ return Calendar.MILLISECOND;
+ }
+
+ /**
+ * Parses a timestamp string off the beginning of the string segment 'seg'.
+ * Goes through each possible valid timestamp format until it finds a match.
+ * The position where the parsing left off is stored in pp.
+ *
+ * @param seg The string segment being parsed.
+ * @param pp Where parsing last left off.
+ * @return An object representing a timestamp.
+ */
+ static CalendarP parseDate(SimpleDateFormat[] formats, String seg) {
+ ParsePosition pp = new ParsePosition(0);
+ for (int i = 0; i < formats.length; i++) {
+ SimpleDateFormat f = formats[i];
+ Date d = f.parse(seg, pp);
+ int idx = pp.getIndex();
+ if (idx != 0) {
+ // it only counts if the next character is '-', 'space', or end-of-string.
+ char c = (seg.length() == idx ? 0 : seg.charAt(idx));
+ if (c == 0 || c == '-' || Character.isWhitespace(c))
+ return new CalendarP(d, getPrecisionField(f.toPattern()));
+ }
+ }
+
+ throw new FormattedRuntimeException("Invalid date encountered: ''{0}''", seg);
+ }
+
+ /**
+ * Combines a Calendar with a precision identifier.
+ */
+ private static class CalendarP {
+ public Calendar c;
+ public int precision;
+
+ public CalendarP(Date date, int precision) {
+ c = Calendar.getInstance();
+ c.setTime(date);
+ this.precision = precision;
+ }
+
+ public CalendarP copy() {
+ return new CalendarP(c.getTime(), precision);
+ }
+
+ public CalendarP roll(int field, int amount) {
+ c.add(field, amount);
+ return this;
+ }
+
+ public CalendarP roll(int amount) {
+ return roll(precision, amount);
+ }
+
+ public Calendar getCalendar() {
+ return c;
+ }
+ }
+}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/QueryMenuItem.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/ViewArgs.java
similarity index 64%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/QueryMenuItem.java
copy to juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/ViewArgs.java
index f01d403..6061465 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/QueryMenuItem.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/ViewArgs.java
@@ -10,40 +10,51 @@
// * "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.rest.widget;
+package org.apache.juneau.pojotools;
-import org.apache.juneau.rest.*;
+import static java.util.Collections.*;
+
+import java.util.*;
/**
- * Widget that returns a menu-item drop-down form for entering search/view/sort arguments.
- *
- * <p>
- * The variable it resolves is <js>"$W{QueryMenuItem}"</js>.
- *
- * <h5 class='section'>See Also:</h5>
- * <ul>
- * <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation.PredefinedWidgets}
- * </ul>
+ * Encapsulates arguments for the {@link PojoViewer} class.
*/
-public class QueryMenuItem extends MenuItemWidget {
+public class ViewArgs {
+
+ private final List<String> view;
/**
- * Returns CSS for the tooltips.
+ * Constructor.
+ *
+ * @param viewArgs
+ * View arguments.
+ * <br>Values are column names.
*/
- @Override
- public String getStyle(RestRequest req) throws Exception {
- return super.getStyle(req)
- + "\n"
- + loadStyle("QueryMenuItem.css");
+ public ViewArgs(String...viewArgs) {
+ this(Arrays.asList(viewArgs));
}
- @Override /* MenuItemWidget */
- public String getLabel(RestRequest req) throws Exception {
- return "query";
+ /**
+ * Constructor.
+ *
+ * @param viewArgs
+ * View arguments.
+ * <br>Values are column names.
+ */
+ public ViewArgs(Collection<String> viewArgs) {
+ this.view = unmodifiableList(new ArrayList<>(viewArgs));
}
- @Override /* MenuItemWidget */
- public String getContent(RestRequest req) throws Exception {
- return loadHtml("QueryMenuItem.html");
+ /**
+ * The view columns.
+ *
+ * <p>
+ * The view columns are the list of columns that should be displayed.
+ * An empty list implies all columns should be displayed.
+ *
+ * @return An unmodifiable list of columns to view.
+ */
+ public List<String> getView() {
+ return view;
}
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java
index 4d8c46d..5cd70aa 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ASet.java
@@ -37,6 +37,7 @@ public final class ASet<T> extends LinkedHashSet<T> {
* @param t The initial values.
* @return A new list.
*/
+ @SafeVarargs
public static <T> ASet<T> create(T...t) {
return new ASet<T>().appendAll(t);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoQuery.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoQuery.java
index 0fab839..3f05cf3 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoQuery.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoQuery.java
@@ -292,7 +292,7 @@ public final class PojoQuery {
}
if (cm.isMap()) {
Map m = (Map)o;
- DelegateMap dm = new DelegateMap(session.getClassMetaForObject(m));
+ DelegateMap dm = new DelegateMap(m, session);
for (Map.Entry e : (Set<Map.Entry>)m.entrySet())
dm.put(e.getKey().toString(), e.getValue());
return dm;
diff --git a/juneau-doc/docs/ReleaseNotes/8.0.1.html b/juneau-doc/docs/ReleaseNotes/8.0.1.html
index 9667543..7cebcb0 100644
--- a/juneau-doc/docs/ReleaseNotes/8.0.1.html
+++ b/juneau-doc/docs/ReleaseNotes/8.0.1.html
@@ -54,5 +54,7 @@
<li class='jac'>{@link oajr.BasicRestJena} - Non-servlet equivalent to {@link oajr.BasicRestServletJena}
<li class='jac'>{@link oajr.BasicRestJenaGroup} - Non-servlet equivalent to {@link oajr.BasicRestServletJenaGroup}
</ul>
+ <li>
+ HTML widgets now have access to the <code>RestResponse</code> object if they need access to the output bean.
</ul>
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/AddOrderMenuItem.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/AddOrderMenuItem.java
index 16a0a9c..fd35548 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/AddOrderMenuItem.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/AddOrderMenuItem.java
@@ -29,17 +29,17 @@ import org.apache.juneau.rest.widget.*;
public class AddOrderMenuItem extends MenuItemWidget {
@Override /* MenuItemWidget */
- public String getLabel(RestRequest req) throws Exception {
+ public String getLabel(RestRequest req, RestResponse res) throws Exception {
return "add";
}
@Override /* MenuItemWidget */
- public String getBeforeShowScript(RestRequest req) throws Exception {
+ public String getBeforeShowScript(RestRequest req, RestResponse res) throws Exception {
return loadScript("AddOrderMenuItem_beforeShow.js");
}
@Override /* Widget */
- public Object getContent(RestRequest req) throws Exception {
+ public Object getContent(RestRequest req, RestResponse res) throws Exception {
return div(
form().id("form").action("servlet:/store/order").method(POST).children(
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/AddPetMenuItem.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/AddPetMenuItem.java
index d5b9744..c438410 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/AddPetMenuItem.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/AddPetMenuItem.java
@@ -29,12 +29,12 @@ import org.apache.juneau.rest.widget.*;
public class AddPetMenuItem extends MenuItemWidget {
@Override /* MenuItemWidget */
- public String getLabel(RestRequest req) throws Exception {
+ public String getLabel(RestRequest req, RestResponse res) throws Exception {
return "add";
}
@Override /* Widget */
- public Object getContent(RestRequest req) throws Exception {
+ public Object getContent(RestRequest req, RestResponse res) throws Exception {
return div(
form().id("form").action("servlet:/pet").method(POST).children(
table(
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/UploadPhotoMenuItem.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/UploadPhotoMenuItem.java
index 60fb02b..d5b8cf0 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/UploadPhotoMenuItem.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/rest/UploadPhotoMenuItem.java
@@ -29,12 +29,12 @@ import org.apache.juneau.rest.widget.*;
public class UploadPhotoMenuItem extends MenuItemWidget {
@Override /* MenuItemWidget */
- public String getLabel(RestRequest req) throws Exception {
+ public String getLabel(RestRequest req, RestResponse res) throws Exception {
return "upload";
}
@Override /* Widget */
- public Object getContent(RestRequest req) throws Exception {
+ public Object getContent(RestRequest req, RestResponse res) throws Exception {
return div(
form().id("form").action("servlet:/upload").method(POST).enctype("multipart/form-data").children(
table(
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index a844db3..2c524f9 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -332,9 +332,10 @@ public class BasicRestCallHandler implements RestCallHandler {
* @return The session objects for that request.
*/
@Override /* RestCallHandler */
- public Map<String,Object> getSessionObjects(RestRequest req) {
+ public Map<String,Object> getSessionObjects(RestRequest req, RestResponse res) {
Map<String,Object> m = new HashMap<>();
m.put(RequestVar.SESSION_req, req);
+ m.put(RequestVar.SESSION_res, res);
return m;
}
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandler.java
index d45be08..bc147ba 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandler.java
@@ -103,7 +103,8 @@ public interface RestCallHandler {
* Returns the session objects for the specified request.
*
* @param req The REST request.
+ * @param res The REST response.
* @return The session objects for that request.
*/
- public Map<String,Object> getSessionObjects(RestRequest req);
+ public Map<String,Object> getSessionObjects(RestRequest req, RestResponse res);
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 32a3e60..30cd36c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -2920,11 +2920,11 @@ public final class RestContext extends BeanContext {
*
* Widgets resolve the following variables:
* <ul class='spaced-list'>
- * <li><js>"$W{name}"</js> - Contents returned by {@link Widget#getHtml(RestRequest)}.
- * <li><js>"$W{name.script}"</js> - Contents returned by {@link Widget#getScript(RestRequest)}.
+ * <li><js>"$W{name}"</js> - Contents returned by {@link Widget#getHtml(RestRequest,RestResponse)}.
+ * <li><js>"$W{name.script}"</js> - Contents returned by {@link Widget#getScript(RestRequest,RestResponse)}.
* <br>The script contents are automatically inserted into the <xt><head/script></xt> section
* in the HTML page.
- * <li><js>"$W{name.style}"</js> - Contents returned by {@link Widget#getStyle(RestRequest)}.
+ * <li><js>"$W{name.style}"</js> - Contents returned by {@link Widget#getStyle(RestRequest,RestResponse)}.
* <br>The styles contents are automatically inserted into the <xt><head/style></xt> section
* in the HTML page.
* </ul>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 14fe47d..114d0e8 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -111,6 +111,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
private Swagger swagger;
private SerializerSessionArgs serializerSessionArgs;
private ParserSessionArgs parserSessionArgs;
+ private RestResponse res;
/**
* Constructor.
@@ -1315,7 +1316,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
*/
public VarResolverSession getVarResolverSession() {
if (varSession == null)
- varSession = context.getVarResolver().createSession(context.getCallHandler().getSessionObjects(this));
+ varSession = context.getVarResolver().createSession(context.getCallHandler().getSessionObjects(this, context.getResponse()));
return varSession;
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HtmlDoc.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HtmlDoc.java
index 36728ee..52dda82 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HtmlDoc.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HtmlDoc.java
@@ -557,13 +557,13 @@ public @interface HtmlDoc {
*
* <ul class='spaced-list'>
* <li>
- * <js>"$W{name}"</js> - Contents returned by {@link Widget#getHtml(RestRequest)}.
+ * <js>"$W{name}"</js> - Contents returned by {@link Widget#getHtml(RestRequest,RestResponse)}.
* <li>
- * <js>"$W{name.script}"</js> - Contents returned by {@link Widget#getScript(RestRequest)}.
+ * <js>"$W{name.script}"</js> - Contents returned by {@link Widget#getScript(RestRequest,RestResponse)}.
* <br>The script contents are automatically inserted into the <xt><head/script></xt> section
* in the HTML page.
* <li>
- * <js>"$W{name.style}"</js> - Contents returned by {@link Widget#getStyle(RestRequest)}.
+ * <js>"$W{name.style}"</js> - Contents returned by {@link Widget#getStyle(RestRequest,RestResponse)}.
* <br>The styles contents are automatically inserted into the <xt><head/style></xt> section
* in the HTML page.
* </ul>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestVar.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestVar.java
index cc589b7..b6cf13d 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestVar.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestVar.java
@@ -69,6 +69,11 @@ public class RequestVar extends MultipartResolvingVar {
*/
public static final String SESSION_req = "req";
+ /**
+ * The name of the session or context object that identifies the {@link RestResponse} object.
+ */
+ public static final String SESSION_res = "res";
+
/** The name of this variable. */
public static final String NAME = "R";
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/WidgetVar.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/WidgetVar.java
index d55203f..f0d1288 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/WidgetVar.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/WidgetVar.java
@@ -43,6 +43,7 @@ public class WidgetVar extends SimpleVar {
* The name of the session or context object that identifies the {@link RestRequest} object.
*/
private static final String SESSION_req = "req";
+ private static final String SESSION_res = "res";
/**
* The name of this variable.
@@ -59,6 +60,7 @@ public class WidgetVar extends SimpleVar {
@Override /* Parameter */
public String resolve(VarResolverSession session, String key) throws Exception {
RestRequest req = session.getSessionObject(RestRequest.class, SESSION_req, true);
+ RestResponse res = session.getSessionObject(RestResponse.class, SESSION_res, true);
boolean isScript = false, isStyle = false;
if (key.endsWith(".script")) {
@@ -76,9 +78,9 @@ public class WidgetVar extends SimpleVar {
return "unknown-widget-"+key;
if (isScript)
- return w.getScript(req);
+ return w.getScript(req, res);
if (isStyle)
- return w.getStyle(req);
- return w.getHtml(req);
+ return w.getStyle(req, res);
+ return w.getHtml(req, res);
}
}
\ No newline at end of file
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/ContentTypeMenuItem.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/ContentTypeMenuItem.java
index af35301..bafb48b 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/ContentTypeMenuItem.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/ContentTypeMenuItem.java
@@ -61,12 +61,12 @@ import org.apache.juneau.utils.*;
public class ContentTypeMenuItem extends MenuItemWidget {
@Override /* MenuItemWidget */
- public String getLabel(RestRequest req) {
+ public String getLabel(RestRequest req, RestResponse res) {
return "content-type";
}
@Override /* MenuItemWidget */
- public Div getContent(RestRequest req) {
+ public Div getContent(RestRequest req, RestResponse res) {
Div div = div();
Set<MediaType> l = new TreeSet<>();
for (Serializer s : req.getSerializers().getSerializers())
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
index ad2e8ea..23d6a60 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/MenuItemWidget.java
@@ -35,7 +35,7 @@ public abstract class MenuItemWidget extends Widget {
* Returns the Javascript needed for the show and hide actions of the menu item.
*/
@Override /* Widget */
- public String getScript(RestRequest req) throws Exception {
+ public String getScript(RestRequest req, RestResponse res) throws Exception {
return loadScript("MenuItemWidget.js");
}
@@ -81,11 +81,12 @@ public abstract class MenuItemWidget extends Widget {
* }
* </p>
*
- * @param req The current request.
+ * @param req The HTTP request object.
+ * @param res The HTTP response object.
* @return Javascript code to execute, or <jk>null</jk> if there isn't any.
* @throws Exception
*/
- public String getBeforeShowScript(RestRequest req) throws Exception {
+ public String getBeforeShowScript(RestRequest req, RestResponse res) throws Exception {
return null;
}
@@ -93,33 +94,34 @@ public abstract class MenuItemWidget extends Widget {
* Optional Javascript to execute immediately after a menu item is shown.
*
* <p>
- * Same as {@link #getBeforeShowScript(RestRequest)} except this Javascript gets executed after the popup dialog has become visible.
+ * Same as {@link #getBeforeShowScript(RestRequest,RestResponse)} except this Javascript gets executed after the popup dialog has become visible.
*
- * @param req The current request.
+ * @param req The HTTP request object.
+ * @param res The HTTP response object.
* @return Javascript code to execute, or <jk>null</jk> if there isn't any.
* @throws Exception
*/
- public String getAfterShowScript(RestRequest req) throws Exception {
+ public String getAfterShowScript(RestRequest req, RestResponse res) throws Exception {
return null;
}
/**
* Defines a <js>"menu-item"</js> class that needs to be used on the outer element of the HTML returned by the
- * {@link #getHtml(RestRequest)} method.
+ * {@link #getHtml(RestRequest,RestResponse)} method.
*/
@Override /* Widget */
- public String getStyle(RestRequest req) throws Exception {
+ public String getStyle(RestRequest req, RestResponse res) throws Exception {
return loadStyle("MenuItemWidget.css");
}
@Override /* Widget */
- public String getHtml(RestRequest req) throws Exception {
+ public String getHtml(RestRequest req, RestResponse res) throws Exception {
StringBuilder sb = new StringBuilder();
// Need a unique number to define unique function names.
Integer id = null;
- String pre = nullIfEmpty(getBeforeShowScript(req)), post = nullIfEmpty(getAfterShowScript(req));
+ String pre = nullIfEmpty(getBeforeShowScript(req, res)), post = nullIfEmpty(getAfterShowScript(req, res));
sb.append("\n<div class='menu-item'>");
if (pre != null || post != null) {
@@ -140,10 +142,10 @@ public abstract class MenuItemWidget extends Widget {
}
String onclick = (pre == null ? "" : "onPreShow"+id+"();") + "menuClick(this);" + (post == null ? "" : "onPostShow"+id+"();");
sb.append(""
- + "\n\t<a onclick='"+onclick+"'>"+getLabel(req)+"</a>"
+ + "\n\t<a onclick='"+onclick+"'>"+getLabel(req, res)+"</a>"
+ "\n<div class='popup-content'>"
);
- Object o = getContent(req);
+ Object o = getContent(req, res);
if (o instanceof Reader) {
try (Reader r = (Reader)o; Writer w = new StringBuilderWriter(sb)) {
IOUtils.pipe(r, w);
@@ -177,15 +179,17 @@ public abstract class MenuItemWidget extends Widget {
* The label for the menu item as it's rendered in the menu bar.
*
* @param req The HTTP request object.
+ * @param res The HTTP response object.
* @return The menu item label.
* @throws Exception
*/
- public abstract String getLabel(RestRequest req) throws Exception;
+ public abstract String getLabel(RestRequest req, RestResponse res) throws Exception;
/**
* The content of the popup.
*
* @param req The HTTP request object.
+ * @param res The HTTP response object.
* @return
* The content of the popup.
* <br>Can be any of the following types:
@@ -197,5 +201,5 @@ public abstract class MenuItemWidget extends Widget {
* </ul>
* @throws Exception
*/
- public abstract Object getContent(RestRequest req) throws Exception;
+ public abstract Object getContent(RestRequest req, RestResponse res) throws Exception;
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByApache.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByApache.java
index a237919..cb68a0f 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByApache.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByApache.java
@@ -53,7 +53,7 @@ public class PoweredByApache extends Widget {
* Returns an Apache image tag hyperlinked to <js>"http://apache.org"</js>
*/
@Override /* Widget */
- public String getHtml(RestRequest req) throws Exception {
+ public String getHtml(RestRequest req, RestResponse res) throws Exception {
UriResolver r = req.getUriResolver();
return "<a href='http://apache.org'><img style='float:right;padding-right:20px;height:32px' src='"+r.resolve("servlet:/htdocs/asf.png")+"'>";
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
index 5231a47..9b2d5c5 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/PoweredByJuneau.java
@@ -33,7 +33,7 @@ public class PoweredByJuneau extends Widget {
* Returns an Apache Juneau image tag hyperlinked to <js>"http://juneau.apache.org"</js>
*/
@Override /* Widget */
- public String getHtml(RestRequest req) throws Exception {
+ public String getHtml(RestRequest req, RestResponse res) throws Exception {
UriResolver r = req.getUriResolver();
return "<a href='http://juneau.apache.org'><img style='float:right;padding-right:20px;height:32px' src='"+r.resolve("servlet:/htdocs/images/juneau.png")+"'>";
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/QueryMenuItem.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/QueryMenuItem.java
index f01d403..d14f142 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/QueryMenuItem.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/QueryMenuItem.java
@@ -31,19 +31,19 @@ public class QueryMenuItem extends MenuItemWidget {
* Returns CSS for the tooltips.
*/
@Override
- public String getStyle(RestRequest req) throws Exception {
- return super.getStyle(req)
+ public String getStyle(RestRequest req, RestResponse res) throws Exception {
+ return super.getStyle(req, res)
+ "\n"
+ loadStyle("QueryMenuItem.css");
}
@Override /* MenuItemWidget */
- public String getLabel(RestRequest req) throws Exception {
+ public String getLabel(RestRequest req, RestResponse res) throws Exception {
return "query";
}
@Override /* MenuItemWidget */
- public String getContent(RestRequest req) throws Exception {
+ public String getContent(RestRequest req, RestResponse res) throws Exception {
return loadHtml("QueryMenuItem.html");
}
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/ThemeMenuItem.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/ThemeMenuItem.java
index b9e495a..7ce1c68 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/ThemeMenuItem.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/ThemeMenuItem.java
@@ -34,12 +34,12 @@ public class ThemeMenuItem extends MenuItemWidget {
private static final String[] BUILT_IN_STYLES = {"devops", "light", "original", "dark"};
@Override /* Widget */
- public String getLabel(RestRequest req) {
+ public String getLabel(RestRequest req, RestResponse res) {
return "themes";
}
@Override /* MenuItemWidget */
- public Div getContent(RestRequest req) throws Exception {
+ public Div getContent(RestRequest req, RestResponse res) throws Exception {
Div div = div();
for (String s : BUILT_IN_STYLES) {
java.net.URI uri = req.getUri(true, new AMap<String,String>().append("stylesheet", "htdocs/themes/"+s+".css"));
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/Widget.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/Widget.java
index 2b60969..85bb1e1 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/Widget.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/Widget.java
@@ -65,10 +65,11 @@ public abstract class Widget {
* A returned value of <jk>null</jk> will cause nothing to be added to the page.
*
* @param req The HTTP request object.
+ * @param res The current HTTP response.
* @return The HTML content of this widget.
* @throws Exception
*/
- public String getHtml(RestRequest req) throws Exception {
+ public String getHtml(RestRequest req, RestResponse res) throws Exception {
return null;
}
@@ -79,10 +80,11 @@ public abstract class Widget {
* A returned value of <jk>null</jk> will cause nothing to be added to the page.
*
* @param req The HTTP request object.
+ * @param res The current HTTP response.
* @return The Javascript needed by this widget.
* @throws Exception
*/
- public String getScript(RestRequest req) throws Exception {
+ public String getScript(RestRequest req, RestResponse res) throws Exception {
return null;
}
@@ -93,10 +95,11 @@ public abstract class Widget {
* A returned value of <jk>null</jk> will cause nothing to be added to the page.
*
* @param req The HTTP request object.
+ * @param res The current HTTP response.
* @return The CSS styles needed by this widget.
* @throws Exception
*/
- public String getStyle(RestRequest req) throws Exception {
+ public String getStyle(RestRequest req, RestResponse res) throws Exception {
return null;
}
@@ -169,11 +172,12 @@ public abstract class Widget {
* </ul>
*
* @param req The current HTTP request.
+ * @param res The current HTTP response.
* @param name Name of the desired resource.
* @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
* @throws IOException
*/
- protected String loadScriptWithVars(RestRequest req, String name) throws IOException {
+ protected String loadScriptWithVars(RestRequest req, RestResponse res, String name) throws IOException {
return req.getVarResolverSession().resolve(loadScript(name));
}
@@ -205,11 +209,12 @@ public abstract class Widget {
* </ul>
*
* @param req The current HTTP request.
+ * @param res The current HTTP response.
* @param name Name of the desired resource.
* @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
* @throws IOException
*/
- protected String loadStyleWithVars(RestRequest req, String name) throws IOException {
+ protected String loadStyleWithVars(RestRequest req, RestResponse res, String name) throws IOException {
return req.getVarResolverSession().resolve(loadStyle(name));
}
@@ -241,11 +246,12 @@ public abstract class Widget {
* </ul>
*
* @param req The current HTTP request.
+ * @param res The current HTTP response.
* @param name Name of the desired resource.
* @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
* @throws IOException
*/
- protected String loadHtmlWithVars(RestRequest req, String name) throws IOException {
+ protected String loadHtmlWithVars(RestRequest req, RestResponse res, String name) throws IOException {
return req.getVarResolverSession().resolve(loadHtml(name));
}
}