http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$MapMatcher.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$MapMatcher.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$MapMatcher.class deleted file mode 100755 index e1303c6..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$MapMatcher.class and /dev/null differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberMatcher.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberMatcher.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberMatcher.class deleted file mode 100755 index d67f612..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberMatcher.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberPattern.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberPattern.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberPattern.class deleted file mode 100755 index 25153ec..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberPattern.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberRange.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberRange.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberRange.class deleted file mode 100755 index 127742c..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$NumberRange.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$ObjectMatcher.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$ObjectMatcher.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$ObjectMatcher.class deleted file mode 100755 index f826750..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$ObjectMatcher.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$SearchPattern.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$SearchPattern.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$SearchPattern.class deleted file mode 100755 index eead3cd..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$SearchPattern.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$StringMatcher.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$StringMatcher.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$StringMatcher.class deleted file mode 100755 index 65b68c5..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$StringMatcher.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$TimestampPattern.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$TimestampPattern.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$TimestampPattern.class deleted file mode 100755 index 98258f3..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$TimestampPattern.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$TimestampRange.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$TimestampRange.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$TimestampRange.class deleted file mode 100755 index 4256f45..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery$TimestampRange.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery.class deleted file mode 100755 index e27b8bf..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery.java deleted file mode 100755 index 4010e64..0000000 --- a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoQuery.java +++ /dev/null @@ -1,1246 +0,0 @@ -/******************************************************************************* - * Licensed Materials - Property of IBM - * © Copyright IBM Corporation 2014, 2015. All Rights Reserved. - * - * The source code for this program is not published or otherwise - * divested of its trade secrets, irrespective of what has been - * deposited with the U.S. Copyright Office. - *******************************************************************************/ -package com.ibm.juno.core.utils; - -import static java.util.Calendar.*; - -import java.text.*; -import java.util.*; -import java.util.regex.*; - -import com.ibm.juno.core.*; - -/** - * Designed to provide query/view/sort/paging filtering on tabular in-memory POJO models. - * <p> - * It can also perform just view filtering on beans/maps. - * <p> - * Examples of tabular POJO models: - * <ul> - * <li><tt>Collection{@code <Map>}</tt> - * <li><tt>Collection{@code <Bean>}</tt> - * <li><tt>Map[]</tt> - * <li><tt>Bean[]</tt> - * </ul> - * <p> - * Tabular POJO models can be thought of as tables of data. For example, a list of the following beans... - * <p class='bcode'> - * <jk>public</jk> MyBean { - * <jk>public int</jk> fi; - * <jk>public</jk> String fs; - * <jk>public</jk> Date fd; - * } - * <p> - * ... can be thought of a table containing the following columns... - * <p> - * <table class='styled code'> - * <tr><th>fi</th><th>fs</th><th>fd</th></tr> - * <tr><td>123</td><td>'foobar'</td><td>yyyy/MM/dd HH:mm:ss</td></tr> - * <tr><td colspan=3>...</td></tr> - * </table> - * <p> - * From this table, you can perform the following functions: - * <ul> - * <li>Query - Return only rows where a search pattern matches. - * <li>View - Return only the specified subset of columns in the specified order. - * <li>Sort - Sort the table by one or more columns. - * <li>Page - Only return a subset of rows. - * </ul> - * - * <h5 class='topic'>Query</h5> - * <p> - * The query capabilites allow you to filter based on query patterns against - * strings, dates, and numbers. Queries take the form of a Map with column names - * as keys, and search patterns as values. <br> - * Search patterns can be either {@code Strings} or {@code Maps}.<br> - * Multiple search patterns are ANDed (i.e. all patterns must match for the row to be returned). - * - * <h6 class='topic'>Examples</h6> - * <ul> - * <li><tt>{fi:'123'}</tt> - Return only rows where the <tt>fi</tt> column is 123. - * <li><tt>{fs:'foobar'}</tt> - Return only rows where the <tt>fs</tt> column is 'foobar'. - * <li><tt>{fd:'2001'}</tt> - Return only rows where the <tt>fd</tt> column have dates in the year 2001. - * <li><tt>{fs:'foobar'}</tt> - Return only rows where the <tt>fs</tt> column is 'foobar'. - * and the <tt>fs</tt> column starts with <tt>"foo"</tt>. - * </ul> - * <p> - * Search patterns can also be applied to lower level fields. For example, the search term - * <tt>{f1:{f2:{f3{'foobar'}}}</tt> means only return top level rows where the <tt>f1.getF2().getF3()</tt> - * property is <tt>'foobar'</tt>. - * - * <h5 class='topic'>String Patterns</h5> - * <p> - * Any objects can be queried against using string patterns. If the objects being - * searched are not strings, then the patterns are matched against whatever is - * return by the {@code Object#toString()} method. - * - * <h6 class='topic'>Example string query patterns</h6> - * <ul> - * <li><tt>foo</tt> - The string 'foo' - * <li><tt>foo bar</tt> - The string 'foo' or the string 'bar' - * <li><tt>'foo bar'</tt> - The phrase 'foo bar' - * <li><tt>"foo bar"</tt> - The phrase 'foo bar' - * <li><tt>foo*</tt> - <tt>*</tt> matches zero-or-more characters. - * <li><tt>foo?</tt> - <tt>?</tt> matches exactly one character - * </ul> - * - * <h6 class='topic'>Notes</h6> - * <ul> - * <li>Whitespace is ignored around search patterns. - * <li>Prepend <tt>+</tt> to tokens that must match. (e.g. <tt>+foo* +*bar</tt>) - * <li>Prepend <tt>-</tt> to tokens that must not match. (e.g. <tt>+foo* -*bar</tt>) - * </ul> - * - * <h5 class='topic'>Numeric Patterns</h5> - * <p> - * Any object of type {@link Number} (or numeric primitives) can be searched using numeric patterns. - * - * <h6 class='topic'>Example numeric query patterns</h6> - * <ul> - * <li><tt>123</tt> - The single number 123 - * <li><tt>1 2 3</tt> - 1, 2, or 3 - * <li><tt>1-100</tt> - Between 1 and 100 - * <li><tt>1 - 100</tt> - Between 1 and 100 - * <li><tt>1 - 100 200-300</tt> - Between 1 and 100 or between 200 and 300 - * <li><tt>> 100</tt> - Greater than 100 - * <li><tt>>= 100</tt> - Greater than or equal to 100 - * <li><tt>!123</tt> - Not 123 - * </ul> - * - * <h6 class='topic'>Notes</h6> - * <ul> - * <li>Whitespace is ignored in search patterns. - * <li>Negative numbers are supported. - * </ul> - * - * <h5 class='topic'>Date Patterns</h5> - * <p> - * Any object of type {@link Date} or {@link Calendar} can be searched using date patterns. - * <p> - * The default valid input timestamp formats (which can be overridden via the {@link #setValidTimestampFormats(String...)} method are... - * - * <ul> - * <li><tt>yyyy.MM.dd.HH.mm.ss</tt> - * <li><tt>yyyy.MM.dd.HH.mm</tt> - * <li><tt>yyyy.MM.dd.HH</tt> - * <li><tt>yyyy.MM.dd</tt> - * <li><tt>yyyy.MM</tt> - * <li><tt>yyyy</tt> - * </ul> - * - * <h6 class='topic'>Example date query patterns</h6> - * <ul> - * <li><tt>2001</tt> - A specific year. - * <li><tt>2001.01.01.10.50</tt> - A specific time. - * <li><tt>>2001</tt> - After a specific year. - * <li><tt>>=2001</tt> - During or after a specific year. - * <li><tt>2001 - 2003.06.30</tt> - A date range. - * <li><tt>2001 2003 2005</tt> - Multiple date patterns are ORed. - * </ul> - * - * <h6 class='topic'>Notes</h6> - * <ul> - * <li>Whitespace is ignored in search patterns. - * </ul> - * - * <h5 class='topic'>View</h5> - * <p> - * The view capability allows you to return only the specified subset of columns in the - * specified order.<br> - * The view parameter is a list of either <tt>Strings</tt> or <tt>Maps</tt>. - * - * <h6 class='topic'>Example view parameters</h6> - * <ul> - * <li><tt>['f1']</tt> - Return only column 'f1'. - * <li><tt>['f2','f1']</tt> - Return only columns 'f2' and 'f1'. - * <li><tt>['f1',{f2:'f3'}]</tt> - Return only columns 'f1' and 'f2', but for 'f2' objects, - * only show the 'f3' property. - * </ul> - * - * <h5 class='topic'>Sort</h5> - * <p> - * The sort capability allows you to sort values by the specified rows.<br> - * The sort parameter is a list of either <tt>Strings</tt> or <tt>Maps</tt>.<br> - * <tt>Strings</tt> represent column names to sort ascending. If you want - * to sort descending, you need to specify a <tt>Map</tt> of the form <tt>{colname:'d'}</tt> - * - * <h6 class='topic'>Example sort parameters</h6> - * <ul> - * <li><tt>['f1']</tt> - Sort rows by column 'f1' ascending. - * <li><tt>[{f1:'a'}]</tt> - Sort rows by column 'f1' ascending. - * <li><tt>[{f1:'d'}]</tt> - Sort rows by column 'f1' descending. - * <li><tt>[{f1:'a'},{f2:'d'}]</tt> - Sort rows by column 'f1' ascending, then 'f2' descending. - * </ul> - * - * <h5 class='topic'>Paging</h5> - * <p> - * Use the <tt>pos</tt> and <tt>limit</tt> parameters to specify a subset of rows to - * return. - * - * <h5 class='topic'>Other Notes</h5> - * <ul> - * <li>Calling <tt>filterMap()</tt> or <tt>filterCollection()</tt> always returns a new data - * structure, so the methods can be called multiple times against the same input. - * </ul> - * - * @author James Bognar ([email protected]) - */ -@SuppressWarnings({"unchecked","rawtypes"}) -public final class PojoQuery { - - private Object input; - private ClassMeta type; - private BeanContext beanContext; - - /** - * Constructor. - * - * @param input The POJO we're going to be filtering. - * @param beanContext The bean context to use to create bean maps for beans. - */ - public PojoQuery(Object input, BeanContext beanContext) { - this.input = input; - this.type = beanContext.getClassMetaForObject(input); - this.beanContext = beanContext; - } - - /** - * Filters the input object as a map. - * - * @param view The list and order of properties to return from the map. Values must be of type {@code String} or {@code Map}. - * @return The filtered map - */ - public Map filterMap(List view) { - - if (input == null) - return null; - - if (! (type.isBean() || type.isMap())) - throw new RuntimeException("Cannot call filterMap() on class type " + type); - - Map m = (Map)replaceWithMutables(input); - doView(m, view); - - return m; - } - - /** - * Filters the input object as a collection of maps. - * - * @param query The query attributes. Keys must be column names and values must be of type {@code String} or {@code Map}. - * @param view The view attributes. Values must be of type {@code String} or {@code Map}. - * @param sort The sort attributes. Values must be of type {@code String} or {@code Map}. - * @param pos The index into the list to start returning results from. Default is {@code 0}. - * @param limit The number of rows to return. Default is all rows. - * @param ignoreCase If <jk>true</jk>, then querying is case insensitive. Default is <jk>false</jk>. - * @return The filtered collection. - */ - public List filterCollection(Map query, List view, List sort, int pos, int limit, boolean ignoreCase) { - - if (input == null) - return null; - - if (! (type.isArray() || type.isCollection())) - throw new RuntimeException("Cannot call filterCollection() on class type " + type); - - if (view == null) - view = Collections.EMPTY_LIST; - - if (sort == null) - sort = Collections.EMPTY_LIST; - - // Create a new ObjectList - ObjectList l = (ObjectList)replaceWithMutables(input); - - // Do the search - CollectionFilter filter = new CollectionFilter(query, ignoreCase); - filter.doQuery(l); - - // If sort or view isn't empty, then we need to make sure that all entries in the - // list are maps. - if ((! sort.isEmpty()) || (! view.isEmpty())) { - - if (! sort.isEmpty()) - doSort(l, sort); - - if (! view.isEmpty()) - doView(l, view); - } - - // Do the paging. - if (pos != 0 || limit != 0) { - int end = (limit == 0 || limit+pos >= l.size()) ? l.size() : limit + pos; - ObjectList l2 = new DelegateList(((DelegateList)l).getClassMeta()); - l2.addAll(l.subList(pos, end)); - l = l2; - } - - return l; - } - - /* - * If there are any non-Maps in the specified list, replaces them with BeanMaps. - */ - private Object replaceWithMutables(Object o) { - if (o == null) - return null; - ClassMeta cm = beanContext.getClassMetaForObject(o); - if (cm.isCollection()) { - ObjectList l = new DelegateList(beanContext.getClassMetaForObject(o)); - for (Object o2 : (Collection)o) - l.add(replaceWithMutables(o2)); - return l; - } - if (cm.isMap() && o instanceof BeanMap) { - BeanMap bm = (BeanMap)o; - DelegateBeanMap dbm = new DelegateBeanMap(bm.getBean(), beanContext); - for (BeanMapEntry e : (Set<BeanMapEntry>)bm.entrySet()) { - ClassMeta ct1 = e.getMeta().getClassMeta(); - if (ct1.isArray() || ct1.isBean() || ct1.isCollection() || ct1.isMap() || ct1.isObject()) - dbm.put(e.getKey(), replaceWithMutables(e.getValue())); - else - dbm.addKey(e.getKey()); - } - return dbm; - } - if (cm.isBean()) { - BeanMap bm = beanContext.forBean(o); - DelegateBeanMap dbm = new DelegateBeanMap(bm.getBean(), beanContext); - for (BeanMapEntry e : (Set<BeanMapEntry>)bm.entrySet()) { - ClassMeta ct1 = e.getMeta().getClassMeta(); - if (ct1.isArray() || ct1.isBean() || ct1.isCollection() || ct1.isMap() || ct1.isObject()) { - Object val = null; - try { - val = e.getValue(); - } catch (BeanRuntimeException ex) { - // Ignore. - } - dbm.put(e.getKey(), replaceWithMutables(val)); - } - else - dbm.addKey(e.getKey()); - } - return dbm; - } - if (cm.isMap()) { - Map m = (Map)o; - DelegateMap dm = new DelegateMap(beanContext.getClassMetaForObject(m)); - for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) - dm.put(e.getKey().toString(), replaceWithMutables(e.getValue())); - return dm; - } - if (cm.isArray()) { - return replaceWithMutables(Arrays.asList((Object[])o)); - } - return o; - } - - /* - * Sorts the specified list by the sort list. - */ - private void doSort(List list, List sortList) { - - Map sort = new LinkedHashMap(); - for (Object s : sortList) { - if (s instanceof String) - sort.put(s, "a"); - else if (s instanceof Map) { - Map sm = (Map)s; - for (Map.Entry e : (Set<Map.Entry>)sm.entrySet()) - sort.put(e.getKey(), e.getValue().toString().toLowerCase(Locale.ENGLISH)); - } - } - - // Do the sort. - List<String> columns = new ArrayList<String>(sort.keySet()); - Collections.reverse(columns); - for (final String c : columns) { - final boolean isDesc = StringUtils.startsWith(sort.get(c).toString(), 'd'); - Comparator comp = new Comparator<Map>() { - @Override /* Comparator */ - public int compare(Map m1, Map m2) { - Comparable v1 = (Comparable)m1.get(c), v2 = (Comparable)m2.get(c); - if (v1 == null && v2 == null) - return 0; - if (v1 == null) - return (isDesc ? -1 : 1); - if (v2 == null) - return (isDesc ? 1 : -1); - return (isDesc ? v2.compareTo(v1) : v1.compareTo(v2)); - } - }; - Collections.sort(list, comp); - } - } - - /* - * Filters all but the specified view columns on all entries in the specified list. - */ - private void doView(List list, List view) { - - for (ListIterator i = list.listIterator(); i.hasNext();) { - Object o = i.next(); - Map m = (Map)o; - doView(m, view); - } - } - - /* - * Creates a new Map with only the entries specified in the view list. - */ - private void doView(Map m, List view) { - List<String> filterKeys = new LinkedList<String>(); - for (Object v : view) { - if (v instanceof String) { - filterKeys.add(v.toString()); - } else if (v instanceof ObjectMap) { - ObjectMap vm = (ObjectMap)v; - for (Map.Entry<String,Object> e : vm.entrySet()) { - String vmKey = e.getKey(); - Object vmVal = e.getValue(); - Object mv = m.get(vmKey); - filterKeys.add(vmKey); - if (vmVal instanceof List) { - List l = (List)vmVal; - if (mv instanceof List) - doView((List)mv, l); - else if (mv instanceof Map) - doView((Map)mv, l); - } - } - } - } - if (m instanceof DelegateMap) - ((DelegateMap)m).filterKeys(filterKeys); - else - ((DelegateBeanMap)m).filterKeys(filterKeys); - } - - - /* - * Returns the appropriate IMatcher for the specified class type. - */ - private IMatcher getObjectMatcherForType(String queryString, boolean ignoreCase, ClassMeta cm) { - if (cm.isDate()) - return new DateMatcher(queryString); - if (cm.isNumber()) - return new NumberMatcher(queryString); - if (cm.isObject()) - return new ObjectMatcher(queryString, ignoreCase); - return new StringMatcher(queryString, ignoreCase); - } - - //==================================================================================================== - // CollectionFilter - //==================================================================================================== - private class CollectionFilter { - IMatcher entryMatcher; - - public CollectionFilter(Map query, boolean ignoreCase) { - if (query != null && ! query.isEmpty()) - entryMatcher = new MapMatcher(query, ignoreCase); - } - - public void doQuery(List in) { - if (in == null || entryMatcher == null) - return; - for (Iterator i = in.iterator(); i.hasNext();) { - Object o = i.next(); - if (! entryMatcher.matches(o)) - i.remove(); - } - } - } - - //==================================================================================================== - // IMatcher - //==================================================================================================== - private interface IMatcher<E> { - public boolean matches(E o); - } - - //==================================================================================================== - // MapMatcher - //==================================================================================================== - /* - * Matches on a Map only if all specified entry matchers match. - */ - private class MapMatcher implements IMatcher<Map> { - - Map<String,IMatcher> entryMatchers = new HashMap<String,IMatcher>(); - - public MapMatcher(Map query, boolean ignoreCase) { - for (Map.Entry e : (Set<Map.Entry>)query.entrySet()) { - String key = e.getKey().toString(); - Object value = e.getValue(); - IMatcher matcher = null; - if (value instanceof String) - matcher = getObjectMatcherForType((String)value, ignoreCase, beanContext.object()); - else if (value instanceof ObjectMap) - matcher = new MapMatcher((ObjectMap)value, ignoreCase); - else - throw new RuntimeException("Invalid value type: " + value); - entryMatchers.put(key, matcher); - } - } - - @Override /* IMatcher */ - public boolean matches(Map m) { - if (m == null) - return false; - for (Map.Entry<String,IMatcher> e : entryMatchers.entrySet()) { - String key = e.getKey(); - Object val = m.get(key); - if (! e.getValue().matches(val)) - return false; - } - return true; - } - } - - //==================================================================================================== - // 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 ObjectMatcher implements IMatcher<Object> { - - String searchPattern; - boolean ignoreCase; - DateMatcher dateMatcher; - NumberMatcher numberMatcher; - StringMatcher stringMatcher; - - ObjectMatcher(String searchPattern, boolean ignoreCase) { - this.searchPattern = searchPattern; - this.ignoreCase = ignoreCase; - } - - @Override /* IMatcher */ - public boolean matches(Object o) { - if (o instanceof Number) - return getNumberMatcher().matches(o); - if (o instanceof Date || o instanceof Calendar) - return getDateMatcher().matches(o); - return getStringMatcher().matches(o); - } - - private IMatcher getNumberMatcher() { - if (numberMatcher == null) - numberMatcher = new NumberMatcher(searchPattern); - return numberMatcher; - } - - private IMatcher getStringMatcher() { - if (stringMatcher == null) - stringMatcher = new StringMatcher(searchPattern, ignoreCase); - return stringMatcher; - } - - private IMatcher getDateMatcher() { - if (dateMatcher == null) - dateMatcher = new DateMatcher(searchPattern); - return dateMatcher; - } - } - - //==================================================================================================== - // NumberMatcher - //==================================================================================================== - private static class NumberMatcher implements IMatcher<Number> { - - private NumberPattern[] numberPatterns; - - /** - * Construct a number matcher for the given search pattern. - * @param searchPattern A date search paattern. See class usage for a description. - */ - public NumberMatcher(String searchPattern) { - numberPatterns = new NumberPattern[1]; - numberPatterns[0] = new NumberPattern(searchPattern); - - } - - /** - * Returns 'true' if this integer matches the pattern(s). - */ - @Override /* IMatcher */ - public boolean matches(Number in) { - for (int i = 0; i < numberPatterns.length; i++) { - if (! numberPatterns[i].matches(in)) - return false; - } - return true; - } - - } - - /** - * A construct representing a single search pattern. - */ - private static class NumberPattern { - NumberRange[] numberRanges; - - public NumberPattern(String searchPattern) { - - List<NumberRange> l = new LinkedList<NumberRange>(); - - for (String s : breakUpTokens(searchPattern)) { - boolean isNot = (s.charAt(0) == '!'); - String token = s.substring(1); - Pattern p = Pattern.compile("(([<>]=?)?)(-?\\d+)(-?(-?\\d+)?)"); - - // Possible patterns: - // 123, >123, <123, >=123, <=123, >-123, >=-123, 123-456, -123--456 - // Regular expression used: (([<>]=?)?)(-?\d+)(-??(-?\d+)) - Matcher m = p.matcher(token); - - // If a non-numeric value was passed in for a numeric value, just set the value to '0'. - // (I think this might resolve a workaround in custom queries). - if (! m.matches()) - throw new RuntimeException("Numeric value didn't match pattern: ["+token+"]"); - //m = numericPattern.matcher("0"); - - String arg1 = m.group(1); - String start = m.group(3); - String end = m.group(5); - - l.add(new NumberRange(arg1, start, end, isNot)); - } - - numberRanges = l.toArray(new NumberRange[l.size()]); - } - - private List<String> breakUpTokens(String s) { - // Get rid of whitespace in "123 - 456" - s = s.replaceAll("(-?\\d+)\\s*-\\s*(-?\\d+)", "$1-$2"); - // Get rid of whitespace in ">= 123" - s = s.replaceAll("([<>]=?)\\s+(-?\\d+)", "$1$2"); - // Get rid of whitespace in "! 123" - s = s.replaceAll("(!)\\s+(-?\\d+)", "$1$2"); - - // Replace all commas with whitespace - // Allows for alternate notation of: 123,456... - s = s.replaceAll(",", " "); - - String[] s2 = s.split("\\s+"); - - // Make all tokens 'ORed'. There is no way to AND numeric tokens. - for (int i = 0; i < s2.length; i++) - if (! StringUtils.startsWith(s2[i], '!')) - s2[i] = "^"+s2[i]; - - List<String> l = new LinkedList<String>(); - l.addAll(Arrays.asList(s2)); - return l; - } - - public boolean matches(Number number) { - if (numberRanges.length == 0) return true; - for (int i = 0; i < numberRanges.length; i++) - if (numberRanges[i].matches(number)) - 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(String arg, String start, String end, boolean isNot) { - - this.isNot = isNot; - - // 123, >123, <123, >=123, <=123, >-123, >=-123, 123-456, -123--456 - if (arg.equals("") && end == null) { // 123 - this.start = Integer.parseInt(start); - this.end = this.start; - } else if (arg.equals(">")) { - this.start = Integer.parseInt(start)+1; - this.end = Integer.MAX_VALUE; - } else if (arg.equals(">=")) { - this.start = Integer.parseInt(start); - this.end = Integer.MAX_VALUE; - } else if (arg.equals("<")) { - this.start = Integer.MIN_VALUE; - this.end = Integer.parseInt(start)-1; - } else if (arg.equals("<=")) { - this.start = Integer.MIN_VALUE; - this.end = Integer.parseInt(start); - } else { - this.start = Integer.parseInt(start); - this.end = Integer.parseInt(end); - } - } - - public boolean matches(Number n) { - long i = n.longValue(); - boolean b = (i>=start && i<=end); - if (isNot) b = !b; - return b; - } - } - - //==================================================================================================== - // DateMatcher - //==================================================================================================== - /** The list of all valid timestamp formats */ - private SimpleDateFormat[] validTimestampFormats = new SimpleDateFormat[0]; - { - setValidTimestampFormats("yyyy.MM.dd.HH.mm.ss","yyyy.MM.dd.HH.mm","yyyy.MM.dd.HH","yyyy.MM.dd","yyyy.MM","yyyy"); - } - - /** - * Use this method to override the allowed search patterns when used in locales where time formats are - * different. - * @param s A comma-delimited list of valid time formats. - */ - public void setValidTimestampFormats(String...s) { - validTimestampFormats = new SimpleDateFormat[s.length]; - for (int i = 0; i < s.length; i++) - validTimestampFormats[i] = new SimpleDateFormat(s[i]); - } - - private class DateMatcher implements IMatcher<Object> { - - private TimestampPattern[] patterns; - - /** - * Construct a timestamp matcher for the given search pattern. - * @param searchPattern The search pattern. - */ - DateMatcher(String searchPattern) { - patterns = new TimestampPattern[1]; - patterns[0] = new TimestampPattern(searchPattern); - - } - - /** - * Returns <jk>true</jk> if the specified date matches the pattern passed in through - * the contstructor.<br> - * The Object can be of type {@link Date} or {@link Calendar}.<br> - * Always returns <jk>false</jk> on <jk>null</jk> input. - */ - @Override /* IMatcher */ - public boolean matches(Object in) { - if (in == null) return false; - - Calendar c = null; - if (in instanceof Calendar) - c = (Calendar)in; - else if (in instanceof Date) { - c = Calendar.getInstance(); - c.setTime((Date)in); - } else { - return false; - } - for (int i = 0; i < patterns.length; i++) { - if (! patterns[i].matches(c)) - return false; - } - return true; - } - } - - /** - * A construct representing a single search pattern. - */ - private class TimestampPattern { - TimestampRange[] ranges; - List<TimestampRange> l = new LinkedList<TimestampRange>(); - - public TimestampPattern(String s) { - - // Handle special case where timestamp is enclosed in quotes. - // This can occur on hyperlinks created by group-by queries. - // e.g. '2007/01/29 04:17:43 PM' - if (s.charAt(0) == '\'' && s.charAt(s.length()-1) == '\'') - s = s.substring(1, s.length()-1); - - // Pattern for finding <,>,<=,>= - Pattern p1 = Pattern.compile("^\\s*([<>](?:=)?)\\s*(\\S+.*)$"); - // Pattern for finding range dash (e.g. xxx - yyy) - Pattern p2 = Pattern.compile("^(\\s*-\\s*)(\\S+.*)$"); - - // States are... - // 1 - Looking for <,>,<=,>= - // 2 - Looking for single date. - // 3 - Looking for start date. - // 4 - Looking for - - // 5 - Looking for end date. - int state = 1; - - String op = null; - CalendarP startDate = null; - - ParsePosition pp = new ParsePosition(0); - Matcher m = null; - String seg = s; - - while (! seg.equals("") || state != 1) { - if (state == 1) { - m = p1.matcher(seg); - if (m.matches()) { - op = m.group(1); - seg = m.group(2); - state = 2; - } else { - state = 3; - } - } else if (state == 2) { - l.add(new TimestampRange(op, parseDate(seg, pp))); - //tokens.add("^"+op + parseTimestamp(seg, pp)); - seg = seg.substring(pp.getIndex()).trim(); - pp.setIndex(0); - state = 1; - } else if (state == 3) { - startDate = parseDate(seg, pp); - seg = seg.substring(pp.getIndex()).trim(); - pp.setIndex(0); - state = 4; - } else if (state == 4) { - // Look for '-' - m = p2.matcher(seg); - if (m.matches()) { - state = 5; - seg = m.group(2); - } else { - // This is a single date (e.g. 2002/01/01) - l.add(new TimestampRange(startDate)); - state = 1; - } - } else if (state == 5) { - l.add(new TimestampRange(startDate, parseDate(seg, pp))); - seg = seg.substring(pp.getIndex()).trim(); - pp.setIndex(0); - state = 1; - } - } - - ranges = l.toArray(new TimestampRange[l.size()]); - } - - public boolean matches(Calendar c) { - if (ranges.length == 0) return true; - 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(CalendarP start, CalendarP end) { - this.start = start.copy().roll(MILLISECOND, -1).getCalendar(); - this.end = end.roll(1).getCalendar(); - } - - public TimestampRange(CalendarP singleDate) { - this.start = singleDate.copy().roll(MILLISECOND, -1).getCalendar(); - this.end = singleDate.roll(1).getCalendar(); - } - - public TimestampRange(String op, CalendarP singleDate) { - if (op.equals(">")) { - this.start = singleDate.roll(1).roll(MILLISECOND, -1).getCalendar(); - this.end = new CalendarP(new Date(Long.MAX_VALUE), 0).getCalendar(); - } else if (op.equals("<")) { - this.start = new CalendarP(new Date(0), 0).getCalendar(); - this.end = singleDate.getCalendar(); - } else if (op.equals(">=")) { - this.start = singleDate.roll(MILLISECOND, -1).getCalendar(); - this.end = new CalendarP(new Date(Long.MAX_VALUE), 0).getCalendar(); - } else if (op.equals("<=")) { - this.start = new CalendarP(new Date(0), 0).getCalendar(); - this.end = singleDate.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 represening a timestamp. - */ - protected CalendarP parseDate(String seg, ParsePosition pp) { - - CalendarP cal = null; - - for (int i = 0; i < validTimestampFormats.length && cal == null; i++) { - pp.setIndex(0); - SimpleDateFormat f = validTimestampFormats[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)) - cal = new CalendarP(d, getPrecisionField(f.toPattern())); - } - } - - if (cal == null) throw new RuntimeException("Invalid date encountered: ["+seg+"]"); - - return cal; - } - - /** - * 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; - } - } - - //==================================================================================================== - // StringMatcher - //==================================================================================================== - private static class StringMatcher implements IMatcher<Object> { - - private SearchPattern[] searchPatterns; - - /** - * Construct a string matcher for the given search pattern. - * @param searchPattern The search pattern. See class usage for details. - * @param ignoreCase If <jk>true</jk>, use case-insensitive matching. - */ - public StringMatcher(String searchPattern, boolean ignoreCase) { - this.searchPatterns = new SearchPattern[1]; - this.searchPatterns[0] = new SearchPattern(searchPattern, ignoreCase); - } - - /** - * Returns 'true' if this string matches the pattern(s). - * Always returns false on null input. - */ - @Override /* IMatcher */ - public boolean matches(Object in) { - if (in == null) return false; - for (int i = 0; i < searchPatterns.length; i++) { - if (! searchPatterns[i].matches(in.toString())) - return false; - } - return true; - } - - } - /** - * A construct representing a single search pattern. - */ - private static class SearchPattern { - Pattern[] orPatterns, andPatterns, notPatterns; - - public SearchPattern(String searchPattern, boolean ignoreCase) { - - List<Pattern> ors = new LinkedList<Pattern>(); - List<Pattern> ands = new LinkedList<Pattern>(); - List<Pattern> nots = new LinkedList<Pattern>(); - - for (String arg : breakUpTokens(searchPattern)) { - char prefix = arg.charAt(0); - String token = arg.substring(1); - - token = token.replaceAll("([\\?\\*\\+\\\\\\[\\]\\{\\}\\(\\)\\^\\$\\.])", "\\\\$1"); - token = token.replace("\u9997", ".*"); - token = token.replace("\u9996", ".?"); - - if (! token.startsWith(".*")) - token = "^" + token; - if (! token.endsWith(".*")) - token = token + "$"; - - int flags = Pattern.DOTALL; - if (ignoreCase) - flags |= Pattern.CASE_INSENSITIVE; - - Pattern p = Pattern.compile(token, flags); - - if (prefix == '^') - ors.add(p); - else if (prefix == '+') - ands.add(p); - else if (prefix == '-') - nots.add(p); - } - orPatterns = ors.toArray(new Pattern[ors.size()]); - andPatterns = ands.toArray(new Pattern[ands.size()]); - notPatterns = nots.toArray(new Pattern[nots.size()]); - } - - /** - * Break up search pattern into separate tokens. - */ - private List<String> breakUpTokens(String s) { - - // If the string is null or all whitespace, return an empty vector. - if (s == null || s.trim().length() == 0) - return Collections.emptyList(); - - // Pad with spaces. - s = " " + s + " "; - - // Replace instances of [+] and [-] inside single and double quotes with - // \u2001 and \u2002 for later replacement. - int escapeCount = 0; - boolean inSingleQuote = false; - boolean inDoubleQuote = false; - char[] ca = s.toCharArray(); - for (int i = 0; i < ca.length; i++) { - if (ca[i] == '\\') escapeCount++; - else if (escapeCount % 2 == 0) { - if (ca[i] == '\'') inSingleQuote = ! inSingleQuote; - else if (ca[i] == '"') inDoubleQuote = ! inDoubleQuote; - else if (ca[i] == '+' && (inSingleQuote || inDoubleQuote)) ca[i] = '\u9999'; - else if (ca[i] == '-' && (inSingleQuote || inDoubleQuote)) ca[i] = '\u9998'; - } - if (ca[i] != '\\') escapeCount = 0; - } - s = new String(ca); - - // Remove spaces between '+' or '-' and the keyword. - //s = perl5Util.substitute("s/([\\+\\-])\\s+/$1/g", s); - s = s.replaceAll("([\\+\\-])\\s+", "$1"); - - // Replace: [*]->[\u3001] as placeholder for '%', ignore escaped. - s = replace(s, '*', '\u9997', true); - // Replace: [?]->[\u3002] as placeholder for '_', ignore escaped. - s = replace(s, '?', '\u9996', true); - // Replace: [\*]->[*], [\?]->[?] - s = unEscapeChars(s, new char[]{'*','?'}); - - // Remove spaces - s = s.trim(); - - // Re-replace the [+] and [-] characters inside quotes. - s = s.replace('\u9999', '+'); - s = s.replace('\u9998', '-'); - - String[] sa = splitQuoted(s, ' '); - List<String> l = new ArrayList<String>(sa.length); - int numOrs = 0; - for (int i = 0; i < sa.length; i++) { - String token = sa[i]; - int len = token.length(); - if (len > 0) { - char c = token.charAt(0); - String s2 = null; - if ((c == '+' || c == '-') && len > 1) - s2 = token.substring(1); - else { - s2 = token; - c = '^'; - numOrs++; - } - // Trim off leading and trailing single and double quotes. - if (s2.matches("\".*\"") || s2.matches("'.*'")) - s2 = s2.substring(1, s2.length()-1); - - // Replace: [\"]->["] - s2 = unEscapeChars(s2, new char[]{'"','\''}); - - // Un-escape remaining escaped backslashes. - s2 = unEscapeChars(s2, new char[]{'\\'}); - - l.add(c + s2); - } - } - - // If there's a single OR clause, turn it into an AND clause (makes the SQL cleaner). - if (numOrs == 1) { - int ii = l.size(); - for (int i = 0; i < ii; i++) { - String x = l.get(i); - if (x.charAt(0) == '^') - l.set(i, '+'+x.substring(1)); - } - } - return l; - } - - public boolean matches(String input) { - if (input == null) return false; - for (int i = 0; i < andPatterns.length; i++) - if (! andPatterns[i].matcher(input).matches()) - return false; - for (int i = 0; i < notPatterns.length; i++) - if (notPatterns[i].matcher(input).matches()) - return false; - for (int i = 0; i < orPatterns.length; i++) - if (orPatterns[i].matcher(input).matches()) - return true; - return orPatterns.length == 0; - } - - } - - /** - * Same as split(String, char), but does not split on characters inside - * single quotes. - * Does not split on escaped delimiters, and escaped quotes are also ignored. - * Example: - * split("a,b,c",',') -> {"a","b","c"} - * split("a,'b,b,b',c",',') -> {"a","'b,b,b'","c"} - */ - private static String[] splitQuoted(String s, char c) { - - if (s == null || s.matches("\\s*")) - return new String[0]; - - List<String> l = new LinkedList<String>(); - char[] sArray = s.toCharArray(); - int x1 = 0; - int escapeCount = 0; - boolean inSingleQuote = false; - boolean inDoubleQuote = false; - for (int i = 0; i < sArray.length; i++) { - if (sArray[i] == '\\') escapeCount++; - else if (escapeCount % 2 == 0) { - if (sArray[i] == '\'' && ! inDoubleQuote) inSingleQuote = ! inSingleQuote; - else if (sArray[i] == '"' && ! inSingleQuote) inDoubleQuote = ! inDoubleQuote; - else if (sArray[i] == c && ! inSingleQuote && ! inDoubleQuote) { - String s2 = new String(sArray, x1, i-x1).trim(); - l.add(s2); - x1 = i+1; - } - } - if (sArray[i] != '\\') escapeCount = 0; - } - String s2 = new String(sArray, x1, sArray.length-x1).trim(); - l.add(s2); - - return l.toArray(new String[l.size()]); - } - - /** - * Replaces tokens in a string with a different token. - * 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 ignoreEscapedChars Specify 'true' if escaped 'from' characters should be ignored. - */ - static String replace(String s, char from, char to, boolean ignoreEscapedChars) { - if (s == null) return null; - - 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); - } - - /** - * Removes escape characters (specified by escapeChar) from the specified characters. - */ - static String unEscapeChars(String s, char[] toEscape) { - char escapeChar = '\\'; - if (s == null) return null; - if (s.length() == 0) return s; - StringBuffer sb = new StringBuffer(s.length()); - char[] sArray = s.toCharArray(); - for (int i = 0; i < sArray.length; i++) { - char c = sArray[i]; - - if (c == escapeChar) { - if (i+1 != sArray.length) { - char c2 = sArray[i+1]; - boolean isOneOf = false; - for (int j = 0; j < toEscape.length && ! isOneOf; j++) - isOneOf = (c2 == toEscape[j]); - if (isOneOf) { - i++; - } else if (c2 == escapeChar) { - sb.append(escapeChar); - i++; - } - } - } - sb.append(sArray[i]); - } - return sb.toString(); - } -} - - http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest$JsonNode.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest$JsonNode.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest$JsonNode.class deleted file mode 100755 index 32b73b8..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest$JsonNode.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest.class deleted file mode 100755 index 3daabb9..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest.java deleted file mode 100755 index 6923d65..0000000 --- a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRest.java +++ /dev/null @@ -1,843 +0,0 @@ -/******************************************************************************* - * Licensed Materials - Property of IBM - * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. - * - * The source code for this program is not published or otherwise - * divested of its trade secrets, irrespective of what has been - * deposited with the U.S. Copyright Office. - *******************************************************************************/ -package com.ibm.juno.core.utils; - -import static java.net.HttpURLConnection.*; - -import java.io.*; -import java.lang.reflect.*; -import java.util.*; - -import com.ibm.juno.core.*; -import com.ibm.juno.core.json.*; -import com.ibm.juno.core.parser.*; - -/** - * Provides the ability to perform standard REST operations (GET, PUT, POST, DELETE) against - * nodes in a POJO model. Nodes in the POJO model are addressed using URLs. - * <p> - * A POJO model is defined as a tree model where nodes consist of consisting of the following: - * <ul> - * <li>{@link Map Maps} and Java beans representing JSON objects. - * <li>{@link Collection Collections} and arrays representing JSON arrays. - * <li>Java beans. - * </ul> - * <p> - * Leaves of the tree can be any type of object. - * <p> - * Use {@link #get(String) get()} to retrieve an element from a JSON tree.<br> - * Use {@link #put(String,Object) put()} to create (or overwrite) an element in a JSON tree.<br> - * Use {@link #post(String,Object) post()} to add an element to a list in a JSON tree.<br> - * Use {@link #delete(String) delete()} to remove an element from a JSON tree.<br> - * <p> - * Leading slashes in URLs are ignored. So <js>"/xxx/yyy/zzz"</js> and <js>"xxx/yyy/zzz"</js> are considered identical. - * - * <h6 class='topic'>Examples</h6> - * <p class='bcode'> - * <jc>// Construct an unstructured POJO model</jc> - * ObjectMap m = <jk>new</jk> ObjectMap(<js>""</js> - * + <js>"{"</js> - * + <js>" name:'John Smith', "</js> - * + <js>" address:{ "</js> - * + <js>" streetAddress:'21 2nd Street', "</js> - * + <js>" city:'New York', "</js> - * + <js>" state:'NY', "</js> - * + <js>" postalCode:10021 "</js> - * + <js>" }, "</js> - * + <js>" phoneNumbers:[ "</js> - * + <js>" '212 555-1111', "</js> - * + <js>" '212 555-2222' "</js> - * + <js>" ], "</js> - * + <js>" additionalInfo:null, "</js> - * + <js>" remote:false, "</js> - * + <js>" height:62.4, "</js> - * + <js>" 'fico score':' > 640' "</js> - * + <js>"} "</js> - * ); - * - * <jc>// Wrap Map inside a PojoRest object</jc> - * PojoRest johnSmith = <jk>new</jk> PojoRest(m); - * - * <jc>// Get a simple value at the top level</jc> - * <jc>// "John Smith"</jc> - * String name = johnSmith.getString(<js>"name"</js>); - * - * <jc>// Change a simple value at the top level</jc> - * johnSmith.put(<js>"name"</js>, <js>"The late John Smith"</js>); - * - * <jc>// Get a simple value at a deep level</jc> - * <jc>// "21 2nd Street"</jc> - * String streetAddress = johnSmith.getString(<js>"address/streetAddress"</js>); - * - * <jc>// Set a simple value at a deep level</jc> - * johnSmith.put(<js>"address/streetAddress"</js>, <js>"101 Cemetery Way"</js>); - * - * <jc>// Get entries in a list</jc> - * <jc>// "212 555-1111"</jc> - * String firstPhoneNumber = johnSmith.getString(<js>"phoneNumbers/0"</js>); - * - * <jc>// Add entries to a list</jc> - * johnSmith.post(<js>"phoneNumbers"</js>, <js>"212 555-3333"</js>); - * - * <jc>// Delete entries from a model</jc> - * johnSmith.delete(<js>"fico score"</js>); - * - * <jc>// Add entirely new structures to the tree</jc> - * ObjectMap medicalInfo = new ObjectMap(<js>""</js> - * + <js>"{"</js> - * + <js>" currentStatus: 'deceased',"</js> - * + <js>" health: 'non-existent',"</js> - * + <js>" creditWorthiness: 'not good'"</js> - * + <js>"}"</js> - * ); - * johnSmith.put(<js>"additionalInfo/medicalInfo"</js>, medicalInfo); - * <p> - * In the special case of collections/arrays of maps/beans, a special XPath-like selector notation - * can be used in lieu of index numbers on GET requests to return a map/bean with a specified attribute value.<br> - * The syntax is {@code @attr=val}, where attr is the attribute name on the child map, and val is the matching value. - * - * <h6 class='topic'>Examples</h6> - * <p class='bcode'> - * <jc>// Get map/bean with name attribute value of 'foo' from a list of items</jc> - * Map m = pojoRest.getMap(<js>"/items/@name=foo"</js>); - * </p> - * - * @author James Bognar ([email protected]) - */ -@SuppressWarnings({"unchecked","rawtypes"}) -public final class PojoRest { - - /** The list of possible request types. */ - private static final int GET=1, PUT=2, POST=3, DELETE=4; - - private ReaderParser parser = JsonParser.DEFAULT; - private final BeanContext bc; - - /** If true, the root cannot be overwritten */ - private boolean rootLocked = false; - - /** The root of the model. */ - private JsonNode root; - - /** - * Create a new instance of a REST interface over the specified object. - * <p> - * Uses {@link BeanContext#DEFAULT} for working with Java beans. - * - * @param o The object to be wrapped. - */ - public PojoRest(Object o) { - this(o, null); - } - - /** - * Create a new instance of a REST interface over the specified object. - * <p> - * The parser is used as the bean context. - * - * @param o The object to be wrapped. - * @param parser The parser to use for parsing arguments and converting objects to the correct data type. - */ - public PojoRest(Object o, ReaderParser parser) { - if (parser == null) - parser = JsonParser.DEFAULT; - this.parser = parser; - this.bc = parser.getBeanContext(); - this.root = new JsonNode(null, null, o, bc.object()); - } - - /** - * Call this method to prevent the root object from being overwritten on put("", xxx); calls. - * - * @return This object (for method chaining). - */ - public PojoRest setRootLocked() { - this.rootLocked = true; - return this; - } - - /** - * The root object that was passed into the constructor of this method. - * - * @return The root object. - */ - public Object getRootObject() { - return root.o; - } - - /** - * Retrieves the element addressed by the URL. - * - * @param url The URL of the element to retrieve. - * If null or blank, returns the root. - * @return The addressed element, or null if that element does not exist in the tree. - */ - public Object get(String url) { - return get(url, null); - } - - /** - * Retrieves the element addressed by the URL. - * - * @param url The URL of the element to retrieve. - * If null or blank, returns the root. - * @param defVal The default value if the map doesn't contain the specified mapping. - * @return The addressed element, or null if that element does not exist in the tree. - */ - public Object get(String url, Object defVal) { - Object o = service(GET, url, null); - return o == null ? defVal : o; - } - - /** - * Retrieves the element addressed by the URL as the specified object type. - * <p> - * Will convert object to the specified type per {@link BeanContext#convertToType(Object, ClassMeta)}. - * - * @param type The specified object type. - * @param url The URL of the element to retrieve. - * If null or blank, returns the root. - * @param <T> The specified object type. - * - * @return The addressed element, or null if that element does not exist in the tree. - */ - public <T> T get(Class<T> type, String url) { - return get(type, url, null); - } - - /** - * Retrieves the element addressed by the URL as the specified object type. - * <p> - * Will convert object to the specified type per {@link BeanContext#convertToType(Object, ClassMeta)}. - * - * @param type The specified object type. - * @param url The URL of the element to retrieve. - * If null or blank, returns the root. - * @param def The default value if addressed item does not exist. - * @param <T> The specified object type. - * - * @return The addressed element, or null if that element does not exist in the tree. - */ - public <T> T get(Class<T> type, String url, T def) { - Object o = service(GET, url, null); - if (o == null) - return def; - return bc.convertToType(o, type); - } - - /** - * Returns the specified entry value converted to a {@link String}. - * <p> - * Shortcut for <code>get(String.<jk>class</jk>, key)</code>. - * - * @param url The key. - * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. - */ - public String getString(String url) { - return get(String.class, url); - } - - /** - * Returns the specified entry value converted to a {@link String}. - * <p> - * Shortcut for <code>get(String.<jk>class</jk>, key, defVal)</code>. - * - * @param url The key. - * @param defVal The default value if the map doesn't contain the specified mapping. - * @return The converted value, or the default value if the map contains no mapping for this key. - */ - public String getString(String url, String defVal) { - return get(String.class, url, defVal); - } - - /** - * Returns the specified entry value converted to an {@link Integer}. - * <p> - * Shortcut for <code>get(Integer.<jk>class</jk>, key)</code>. - * - * @param url The key. - * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public Integer getInt(String url) { - return get(Integer.class, url); - } - - /** - * Returns the specified entry value converted to an {@link Integer}. - * <p> - * Shortcut for <code>get(Integer.<jk>class</jk>, key, defVal)</code>. - * - * @param url The key. - * @param defVal The default value if the map doesn't contain the specified mapping. - * @return The converted value, or the default value if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public Integer getInt(String url, Integer defVal) { - return get(Integer.class, url, defVal); - } - - /** - * Returns the specified entry value converted to a {@link Long}. - * <p> - * Shortcut for <code>get(Long.<jk>class</jk>, key)</code>. - * - * @param url The key. - * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public Long getLong(String url) { - return get(Long.class, url); - } - - /** - * Returns the specified entry value converted to a {@link Long}. - * <p> - * Shortcut for <code>get(Long.<jk>class</jk>, key, defVal)</code>. - * - * @param url The key. - * @param defVal The default value if the map doesn't contain the specified mapping. - * @return The converted value, or the default value if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public Long getLong(String url, Long defVal) { - return get(Long.class, url, defVal); - } - - /** - * Returns the specified entry value converted to a {@link Boolean}. - * <p> - * Shortcut for <code>get(Boolean.<jk>class</jk>, key)</code>. - * - * @param url The key. - * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public Boolean getBoolean(String url) { - return get(Boolean.class, url); - } - - /** - * Returns the specified entry value converted to a {@link Boolean}. - * <p> - * Shortcut for <code>get(Boolean.<jk>class</jk>, key, defVal)</code>. - * - * @param url The key. - * @param defVal The default value if the map doesn't contain the specified mapping. - * @return The converted value, or the default value if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public Boolean getBoolean(String url, Boolean defVal) { - return get(Boolean.class, url, defVal); - } - - /** - * Returns the specified entry value converted to a {@link Map}. - * <p> - * Shortcut for <code>get(Map.<jk>class</jk>, key)</code>. - * - * @param url The key. - * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public Map<?,?> getMap(String url) { - return get(Map.class, url); - } - - /** - * Returns the specified entry value converted to a {@link Map}. - * <p> - * Shortcut for <code>get(Map.<jk>class</jk>, key, defVal)</code>. - * - * @param url The key. - * @param defVal The default value if the map doesn't contain the specified mapping. - * @return The converted value, or the default value if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public Map<?,?> getMap(String url, Map<?,?> defVal) { - return get(Map.class, url, defVal); - } - - /** - * Returns the specified entry value converted to a {@link List}. - * <p> - * Shortcut for <code>get(List.<jk>class</jk>, key)</code>. - * - * @param url The key. - * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public List<?> getList(String url) { - return get(List.class, url); - } - - /** - * Returns the specified entry value converted to a {@link List}. - * <p> - * Shortcut for <code>get(List.<jk>class</jk>, key, defVal)</code>. - * - * @param url The key. - * @param defVal The default value if the map doesn't contain the specified mapping. - * @return The converted value, or the default value if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public List<?> getList(String url, List<?> defVal) { - return get(List.class, url, defVal); - } - - /** - * Returns the specified entry value converted to a {@link Map}. - * <p> - * Shortcut for <code>get(ObjectMap.<jk>class</jk>, key)</code>. - * - * @param url The key. - * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public ObjectMap getObjectMap(String url) { - return get(ObjectMap.class, url); - } - - /** - * Returns the specified entry value converted to a {@link ObjectMap}. - * <p> - * Shortcut for <code>get(ObjectMap.<jk>class</jk>, key, defVal)</code>. - * - * @param url The key. - * @param defVal The default value if the map doesn't contain the specified mapping. - * @return The converted value, or the default value if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public ObjectMap getObjectMap(String url, ObjectMap defVal) { - return get(ObjectMap.class, url, defVal); - } - - /** - * Returns the specified entry value converted to a {@link ObjectList}. - * <p> - * Shortcut for <code>get(ObjectList.<jk>class</jk>, key)</code>. - * - * @param url The key. - * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public ObjectList getObjectList(String url) { - return get(ObjectList.class, url); - } - - /** - * Returns the specified entry value converted to a {@link ObjectList}. - * <p> - * Shortcut for <code>get(ObjectList.<jk>class</jk>, key, defVal)</code>. - * - * @param url The key. - * @param defVal The default value if the map doesn't contain the specified mapping. - * @return The converted value, or the default value if the map contains no mapping for this key. - * @throws InvalidDataConversionException If value cannot be converted. - */ - public ObjectList getObjectList(String url, ObjectList defVal) { - return get(ObjectList.class, url, defVal); - } - - /** - * Executes the specified method with the specified parameters on the specified object. - * - * @param url The URL of the element to retrieve. - * @param method The method signature. - * <p> - * Can be any of the following formats: - * </p> - * <ul> - * <li>Method name only. e.g. <js>"myMethod"</js>. - * <li>Method name with class names. e.g. <js>"myMethod(String,int)"</js>. - * <li>Method name with fully-qualified class names. e.g. <js>"myMethod(java.util.String,int)"</js>. - * </ul> - * <p> - * As a rule, use the simplest format needed to uniquely resolve a method. - * </p> - * @param args The arguments to pass as parameters to the method.<br> - * These will automatically be converted to the appropriate object type if possible.<br> - * This must be an array, like a JSON array. - * @return The returned object from the method call. - * @throws IllegalAccessException If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is inaccessible. - * @throws IllegalArgumentException If one of the following occurs: - * <ul> - * <li>The number of actual and formal parameters differ. - * <li>An unwrapping conversion for primitive arguments fails. - * <li>A parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion. - * <li>The constructor pertains to an enum type. - * </ul> - * @throws InvocationTargetException If the underlying constructor throws an exception. - * @throws ParseException If the input contains a syntax error or is malformed. - * @throws NoSuchMethodException - * @throws IOException - */ - public Object invokeMethod(String url, String method, String args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, ParseException, NoSuchMethodException, IOException { - return new PojoIntrospector(get(url), parser).invokeMethod(method, args); - } - - /** - * Returns the list of available methods that can be passed to the {@link #invokeMethod(String, String, String)} for the object - * addressed by the specified URL. - * - * @param url The URL. - * @return The list of methods. - */ - public Collection<String> getPublicMethods(String url) { - Object o = get(url); - if (o == null) - return null; - return bc.getClassMeta(o.getClass()).getPublicMethods().keySet(); - } - - /** - * Returns the class type of the object at the specified URL. - * - * @param url The URL. - * @return The class type. - */ - public ClassMeta getClassMeta(String url) { - JsonNode n = getNode(normalizeUrl(url), root); - if (n == null) - return null; - return n.cm; - } - - /** - * Sets/replaces the element addressed by the URL. - * <p> - * This method expands the POJO model as necessary to create the new element. - * - * @param url The URL of the element to create. - * If <jk>null</jk> or blank, the root itself is replaced with the specified value. - * @param val The value being set. Value can be of any type. - * @return The previously addressed element, or <jk>null</jk> the element did not previously exist. - */ - public Object put(String url, Object val) { - return service(PUT, url, val); - } - - /** - * Adds a value to a list element in a POJO model. - * <p> - * The URL is the address of the list being added to. - * <p> - * If the list does not already exist, it will be created. - * <p> - * This method expands the POJO model as necessary to create the new element. - * <p> - * Note: You can only post to three types of nodes: - * <ul> - * <li>{@link List Lists} - * <li>{@link Map Maps} containing integers as keys (i.e sparse arrays) - * <li>arrays - * </ul> - * - * @param url The URL of the element being added to. - * If null or blank, the root itself (assuming it's one of the types specified above) is added to. - * @param val The value being added. - * @return The URL of the element that was added. - */ - public String post(String url, Object val) { - return (String)service(POST, url, val); - } - - /** - * Remove an element from a POJO model. - * <p> - * qIf the element does not exist, no action is taken. - * - * @param url The URL of the element being deleted. - * If <jk>null</jk> or blank, the root itself is deleted. - * @return The removed element, or null if that element does not exist. - */ - public Object delete(String url) { - return service(DELETE, url, null); - } - - @Override /* Object */ - public String toString() { - return String.valueOf(root.o); - } - - /** Handle nulls and strip off leading '/' char. */ - private String normalizeUrl(String url) { - - // Interpret nulls and blanks the same (i.e. as addressing the root itself) - if (url == null) - url = ""; - - // Strip off leading slash if present. - if (url.length() > 0 && url.charAt(0) == '/') - url = url.substring(1); - - return url; - } - - - /* - * Workhorse method. - */ - private Object service(int method, String url, Object val) throws PojoRestException { - - url = normalizeUrl(url); - - if (method == GET) { - JsonNode p = getNode(url, root); - return p == null ? null : p.o; - } - - // Get the url of the parent and the property name of the addressed object. - int i = url.lastIndexOf('/'); - String parentUrl = (i == -1 ? null : url.substring(0, i)); - String childKey = (i == -1 ? url : url.substring(i + 1)); - - if (method == PUT) { - if (url.length() == 0) { - if (rootLocked) - throw new PojoRestException(HTTP_FORBIDDEN, "Cannot overwrite root object"); - Object o = root.o; - root = new JsonNode(null, null, val, bc.object()); - return o; - } - JsonNode n = (parentUrl == null ? root : getNode(parentUrl, root)); - if (n == null) - throw new PojoRestException(HTTP_NOT_FOUND, "Node at URL ''{0}'' not found.", parentUrl); - ClassMeta cm = n.cm; - Object o = n.o; - if (cm.isMap()) - return ((Map)o).put(childKey, convert(val, cm.getValueType())); - if (cm.isCollection() && o instanceof List) - return ((List)o).set(parseInt(childKey), convert(val, cm.getElementType())); - if (cm.isArray()) { - o = setArrayEntry(n.o, parseInt(childKey), val, cm.getElementType()); - ClassMeta pct = n.parent.cm; - Object po = n.parent.o; - if (pct.isMap()) { - ((Map)po).put(n.keyName, o); - return url; - } - if (pct.isBean()) { - BeanMap m = bc.forBean(po); - m.put(n.keyName, o); - return url; - } - throw new PojoRestException(HTTP_BAD_REQUEST, "Cannot perform PUT on ''{0}'' with parent node type ''{1}''", url, pct); - } - if (cm.isBean()) - return bc.forBean(o).put(childKey, val); - throw new PojoRestException(HTTP_BAD_REQUEST, "Cannot perform PUT on ''{0}'' whose parent is of type ''{1}''", url, cm); - } - - if (method == POST) { - // Handle POST to root special - if (url.length() == 0) { - ClassMeta cm = root.cm; - Object o = root.o; - if (cm.isCollection()) { - Collection c = (Collection)o; - c.add(convert(val, cm.getElementType())); - return (c instanceof List ? url + "/" + (c.size()-1) : null); - } - if (cm.isArray()) { - Object[] o2 = addArrayEntry(o, val, cm.getElementType()); - root = new JsonNode(null, null, o2, null); - return url + "/" + (o2.length-1); - } - throw new PojoRestException(HTTP_BAD_REQUEST, "Cannot perform POST on ''{0}'' of type ''{1}''", url, cm); - } - JsonNode n = getNode(url, root); - if (n == null) - throw new PojoRestException(HTTP_NOT_FOUND, "Node at URL ''{0}'' not found.", url); - ClassMeta cm = n.cm; - Object o = n.o; - if (cm.isArray()) { - Object[] o2 = addArrayEntry(o, val, cm.getElementType()); - ClassMeta pct = n.parent.cm; - Object po = n.parent.o; - if (pct.isMap()) { - ((Map)po).put(childKey, o2); - return url + "/" + (o2.length-1); - } - if (pct.isBean()) { - BeanMap m = bc.forBean(po); - m.put(childKey, o2); - return url + "/" + (o2.length-1); - } - throw new PojoRestException(HTTP_BAD_REQUEST, "Cannot perform POST on ''{0}'' with parent node type ''{1}''", url, pct); - } - if (cm.isCollection()) { - Collection c = (Collection)o; - c.add(convert(val, cm.getElementType())); - return (c instanceof List ? url + "/" + (c.size()-1) : null); - } - throw new PojoRestException(HTTP_BAD_REQUEST, "Cannot perform POST on ''{0}'' of type ''{1}''", url, cm); - } - - if (method == DELETE) { - if (url.length() == 0) { - if (rootLocked) - throw new PojoRestException(HTTP_FORBIDDEN, "Cannot overwrite root object"); - Object o = root.o; - root = new JsonNode(null, null, null, bc.object()); - return o; - } - JsonNode n = (parentUrl == null ? root : getNode(parentUrl, root)); - ClassMeta cm = n.cm; - Object o = n.o; - if (cm.isMap()) - return ((Map)o).remove(childKey); - if (cm.isCollection() && o instanceof List) - return ((List)o).remove(parseInt(childKey)); - if (cm.isArray()) { - int index = parseInt(childKey); - Object old = ((Object[])o)[index]; - Object[] o2 = removeArrayEntry(o, index); - ClassMeta pct = n.parent.cm; - Object po = n.parent.o; - if (pct.isMap()) { - ((Map)po).put(n.keyName, o2); - return old; - } - if (pct.isBean()) { - BeanMap m = bc.forBean(po); - m.put(n.keyName, o2); - return old; - } - throw new PojoRestException(HTTP_BAD_REQUEST, "Cannot perform POST on ''{0}'' with parent node type ''{1}''", url, pct); - } - if (cm.isBean()) - return bc.forBean(o).put(childKey, null); - throw new PojoRestException(HTTP_BAD_REQUEST, "Cannot perform PUT on ''{0}'' whose parent is of type ''{1}''", url, cm); - } - - return null; // Never gets here. - } - - private Object[] setArrayEntry(Object o, int index, Object val, ClassMeta componentType) { - Object[] a = (Object[])o; - if (a.length <= index) { - // Expand out the array. - Object[] a2 = (Object[])Array.newInstance(a.getClass().getComponentType(), index+1); - System.arraycopy(a, 0, a2, 0, a.length); - a = a2; - } - a[index] = convert(val, componentType); - return a; - } - - private Object[] addArrayEntry(Object o, Object val, ClassMeta componentType) { - Object[] a = (Object[])o; - // Expand out the array. - Object[] a2 = (Object[])Array.newInstance(a.getClass().getComponentType(), a.length+1); - System.arraycopy(a, 0, a2, 0, a.length); - a2[a.length] = convert(val, componentType); - return a2; - } - - private Object[] removeArrayEntry(Object o, int index) { - Object[] a = (Object[])o; - // Shrink the array. - Object[] a2 = (Object[])Array.newInstance(a.getClass().getComponentType(), a.length-1); - System.arraycopy(a, 0, a2, 0, index); - System.arraycopy(a, index+1, a2, index, a.length-index-1); - return a2; - } - - class JsonNode { - Object o; - ClassMeta cm; - JsonNode parent; - String keyName; - - JsonNode(JsonNode parent, String keyName, Object o, ClassMeta cm) { - this.o = o; - this.keyName = keyName; - this.parent = parent; - if (cm == null || cm.isObject()) { - if (o == null) - cm = bc.object(); - else - cm = bc.getClassMetaForObject(o); - } - this.cm = cm; - } - } - - JsonNode getNode(String url, JsonNode n) { - if (url == null || url.isEmpty()) - return n; - int i = url.indexOf('/'); - String parentKey, childUrl = null; - if (i == -1) { - parentKey = url; - } else { - parentKey = url.substring(0, i); - childUrl = url.substring(i + 1); - } - - Object o = n.o; - Object o2 = null; - ClassMeta cm = n.cm; - ClassMeta ct2 = null; - if (o == null) - return null; - if (cm.isMap()) { - o2 = ((Map)o).get(parentKey); - ct2 = cm.getValueType(); - } else if (cm.isCollection() && o instanceof List) { - int key = parseInt(parentKey); - List l = ((List)o); - if (l.size() <= key) - return null; - o2 = l.get(key); - ct2 = cm.getElementType(); - } else if (cm.isArray()) { - int key = parseInt(parentKey); - Object[] a = ((Object[])o); - if (a.length <= key) - return null; - o2 = a[key]; - ct2 = cm.getElementType(); - } else if (cm.isBean()) { - BeanMap m = bc.forBean(o); - o2 = m.get(parentKey); - BeanPropertyMeta pMeta = m.getPropertyMeta(parentKey); - if (pMeta == null) - throw new PojoRestException(HTTP_BAD_REQUEST, - "Unknown property ''{0}'' encountered while trying to parse into class ''{1}''", - parentKey, m.getClassMeta() - ); - ct2 = pMeta.getClassMeta(); - } - - if (childUrl == null) - return new JsonNode(n, parentKey, o2, ct2); - - return getNode(childUrl, new JsonNode(n, parentKey, o2, ct2)); - } - - private Object convert(Object in, ClassMeta cm) { - if (cm == null) - return in; - if (cm.isBean() && in instanceof Map) - return bc.convertToType(in, cm); - return in; - } - - private int parseInt(String key) { - try { - return Integer.parseInt(key); - } catch (NumberFormatException e) { - throw new PojoRestException(HTTP_BAD_REQUEST, - "Cannot address an item in an array with a non-integer key ''{0}''", key - ); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRestException.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRestException.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRestException.class deleted file mode 100755 index a9c026e..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRestException.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRestException.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRestException.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRestException.java deleted file mode 100755 index 86e3934..0000000 --- a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/PojoRestException.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Licensed Materials - Property of IBM - * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. - * - * The source code for this program is not published or otherwise - * divested of its trade secrets, irrespective of what has been - * deposited with the U.S. Copyright Office. - *******************************************************************************/ -package com.ibm.juno.core.utils; - -import java.net.*; -import java.text.*; - -/** - * Generic exception thrown from the {@link PojoRest} class. - * <p> - * Typically, this is a user-error, such as trying to address a non-existent node in the tree. - * <p> - * The status code is an HTTP-equivalent code. It will be one of the following: - * <ul> - * <li>{@link HttpURLConnection#HTTP_BAD_REQUEST HTTP_BAD_REQUEST} - Attempting to do something impossible. - * <li>{@link HttpURLConnection#HTTP_NOT_FOUND HTTP_NOT_FOUND} - Attempting to access a non-existent node in the tree. - * <li>{@link HttpURLConnection#HTTP_FORBIDDEN HTTP_FORBIDDEN} - Attempting to overwrite the root object. - * </ul> - * - * @author James Bognar ([email protected]) - */ -public final class PojoRestException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - private int status; - - /** - * Constructor. - * - * @param status The HTTP-equivalent status code. - * @param message The detailed message. - * @param args Optional message arguments. - */ - public PojoRestException(int status, String message, Object...args) { - super(args.length == 0 ? message : MessageFormat.format(message, args)); - this.status = status; - } - - /** - * The HTTP-equivalent status code. - * <p> - * See above for details. - * - * @return The HTTP-equivalent status code. - */ - public int getStatus() { - return status; - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$1.class deleted file mode 100755 index 72f4d06..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$1.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$2.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$2.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$2.class deleted file mode 100755 index 9f5f469..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$2.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$3.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$3.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$3.class deleted file mode 100755 index b13996a..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$3.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$4.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$4.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$4.class deleted file mode 100755 index c75c48a..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$4.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$5.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$5.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$5.class deleted file mode 100755 index d83e166..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$5.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$Matcher.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$Matcher.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$Matcher.class deleted file mode 100755 index 9521154..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder$Matcher.class and /dev/null differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/30947fd7/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder.class deleted file mode 100755 index 5083520..0000000 Binary files a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ProcBuilder.class and /dev/null differ
