pax_global_header00006660000000000000000000000064115774367040014530gustar00rootroot0000000000000052 comment=375bda84a4c1b5d0d3fb8bcf1efd65007d16b8c2 gentlyweb-utils-1.5/000077500000000000000000000000001157743670400145335ustar00rootroot00000000000000gentlyweb-utils-1.5/LICENSE-2.0.txt000066400000000000000000000261361157743670400166630ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. gentlyweb-utils-1.5/build-utils.xml000066400000000000000000000040301157743670400175070ustar00rootroot00000000000000 Unable to find common jars! Check paths in target "init".
gentlyweb-utils-1.5/src/000077500000000000000000000000001157743670400153225ustar00rootroot00000000000000gentlyweb-utils-1.5/src/utils/000077500000000000000000000000001157743670400164625ustar00rootroot00000000000000gentlyweb-utils-1.5/src/utils/com/000077500000000000000000000000001157743670400172405ustar00rootroot00000000000000gentlyweb-utils-1.5/src/utils/com/gentlyweb/000077500000000000000000000000001157743670400212405ustar00rootroot00000000000000gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/000077500000000000000000000000001157743670400224005ustar00rootroot00000000000000gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/Accessor.java000066400000000000000000000253121157743670400250100ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.StringTokenizer; /** * This class is used to perform access into a Java object using a * String value with a specific notation. *

* The Accessor uses a dot notation such as field1.method1.method2 to * perform the access on an object. Each value in the notation refers to * a field or method (a no argument method) of the type of the previous * value. * For instance if you have the following class structure: *

*
 * public class A 
 * {
 *    public B = new B ();
 * }
 * 
 * public class B
 * {
 *    public C = new C ();
 * }
 * 
 * public class C
 * {
 *    String d = "";
 * }
 * 
*

* You would then use the notation: B.C.d to get access to * field d in Class C. *

* The Accessor also supports a [ ] notation for accessing * into Lists/Maps and Arrays. If the value between the [ ] * is an integer then we look for the associated type to be either * an array or a List, we then index into it with the integer. If * the value is NOT an integer then we use assume the * type is a Map and use it as a key into the Map. *

* For instance changing the example above: *

*
 * public class A 
 * {
 *    public List vals = new ArrayList ();
 * }
 * 
*

* Now we could use: vals[X] where X is a positive integer. * Or changing again: *

*
 * public class A 
 * {
 *    public Map vals = new HashMap ();
 * }
 * 
*

* We could use: vals[VALUE] where VALUE would then be * used as a Key into the vals HashMap. *

* Note: The Accessor is NOT designed to be an all purpose * method of gaining access to a class. It has specific uses and for * most will be of no use at all. It should be used for general purpose * applications where you want to access specific fields of an object * without having to know the exact type. One such application is in * the {@link GeneralComparator}, in that case arbitrary Objects can * be sorted without having to write complex Comparators or implementing * the Comparable interface AND it gives the flexibility that sorting * can be changed ad-hoc. *

* The Accessor looks for in the following order: *

*

* Note: we have had to add the 3rd type to allow for methods that don't follow * JavaBeans conventions (there are loads in the standard Java APIs which makes * accessing impossible otherwise). */ public class Accessor { private Object accessor = null; private String index = ""; public String getIndex () { return this.index; } public void setIndex (String index) { this.index = index; } public void setAccessor (Field f) { this.accessor = f; } public void setAccessor (Method m) { this.accessor = m; } public boolean isIndexCorrectForType (Class c) { // See if the index is a number...in which case // the class should be either an Array or a List. if (this.index.equals ("")) { return true; } try { Integer.parseInt (this.index); if ((c.isArray ()) || (c.isAssignableFrom (List.class)) ) { return true; } } catch (NumberFormatException e) { if (c.isAssignableFrom (Map.class)) { return true; } } return false; } public static Object getValueFromAccessorChain (Object obj, List chain) throws IllegalAccessException, InvocationTargetException { // For our accessor chain, use the Field and Methods // to get the actual value. Object retdata = obj; for (int i = 0; i < chain.size (); i++) { Accessor a = (Accessor) chain.get (i); // See what type the accessor is... if (a.accessor instanceof Method) { Method m = (Method) a.accessor; Object[] parms = {}; // Invoke the method... retdata = m.invoke (retdata, parms); if (retdata == null) { return null; } } if (a.accessor instanceof Field) { // It's a field...so... Field f = (Field) a.accessor; // Now get the value... retdata = f.get (retdata); } // See if we have an index... if (!a.getIndex ().equals ("")) { retdata = Accessor.getValueForIndex (retdata, a.getIndex ()); if (retdata == null) { return null; } } } return retdata; } public static Object getValueForIndex (Object data, String index) { try { int in = Integer.parseInt (index); // See if we have an Array or // List. if (data.getClass ().isArray ()) { if (in < Array.getLength (data)) { // It's an array. return Array.get (data, in); } } else { if (data instanceof List) { List l = (List) data; if (in < l.size ()) { return l.get (in); } } } } catch (NumberFormatException e) { // It's not a number so assume that // we have a Map. if (data instanceof Map) { Map map = (Map) data; return map.get (index); } } return null; } /** * Get the Java field associated with the named field. Return * null if there isn't one, or if we can't access it. * * @param field The name of the field. * @param clazz The Class to get the field from. * @return A List of Accessor objects used for delving into the * classes to be sorted. */ public static List getAccessorChain (String accessorRef, Class clazz) throws IllegalArgumentException { StringTokenizer t = new StringTokenizer (accessorRef, "."); Class c = clazz; List retdata = new ArrayList (); while (t.hasMoreTokens ()) { String tok = t.nextToken (); Accessor a = new Accessor (); String index = ""; if (tok.endsWith ("]")) { // It does, so now find the [ index = tok.substring ((tok.indexOf ("[") + 1), tok.length () - 1); a.setIndex (index); tok = tok.substring (0, tok.indexOf ("[")); } // Get the Fields. Field[] fields = c.getFields (); Field f = null; // See if the token matches... for (int i = 0; i < fields.length; i++) { if (fields[i].getName ().equals (tok)) { // Found it... f = fields[i]; break; } } if (f != null) { c = f.getType (); if (!a.isIndexCorrectForType (c)) { throw new IllegalArgumentException ("Field: " + tok + " cannot be accessed with index: " + index + " since field type is: " + c.getName ()); } a.accessor = f; retdata.add (a); } else { // Now convert it to a method name and use the // JavaBeans convention... StringBuffer name = new StringBuffer (tok); name.setCharAt (0, Character.toUpperCase (tok.charAt (0))); name.insert (0, "get"); // Now get the method... Method m = Accessor.getNoParmJavaMethod (name.toString (), c); if (m != null) { c = m.getReturnType (); if (!a.isIndexCorrectForType (c)) { throw new IllegalArgumentException ("Method: " + tok + " cannot be called with index: " + index + " since method return type is: " + c.getName ()); } if (Void.class.isAssignableFrom (c)) { throw new IllegalArgumentException ("Method: " + tok + " cannot be called on class: " + c.getName () + " since return type is void"); } a.accessor = m; retdata.add (a); continue; } else { // The field is not either a standard JavaBeans method so now just // look for the method with the specified name. m = Accessor.getNoParmJavaMethod (tok, c); if (m == null) { throw new IllegalArgumentException ("Cannot find method with name: " + tok + " in class: " + c.getName ()); } c = m.getReturnType (); if (!a.isIndexCorrectForType (c)) { throw new IllegalArgumentException ("Method: " + tok + " cannot be called with index: " + index + " since method return type is: " + c.getName ()); } if (Void.class.isAssignableFrom (c)) { throw new IllegalArgumentException ("Method: " + tok + " cannot be called on class: " + c.getName () + " since return type is void"); } a.accessor = m; retdata.add (a); continue; } } } return retdata; } private static Method getNoParmJavaMethod (String method, Class c) { Method[] methods = c.getMethods (); for (int i = 0; i < methods.length; i++) { if ((methods[i].getName ().equals (method)) && (methods[i].getParameterTypes ().length == 0) ) { // This is the one... return methods[i]; } } return null; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/ChainException.java000066400000000000000000000030241157743670400261430ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.io.PrintWriter; import java.io.IOException; public class ChainException extends Exception { private Throwable exp = null; public ChainException (String message) { super (message); } public ChainException (String message, Throwable exp) { super (message, exp); this.exp = exp; } public Throwable getException () { return this.exp; } public void printInnerExceptionChain (PrintWriter out) throws IOException { // Print out the inner exceptions if they exist... if (this.exp != null) { out.println ("Next exception in chain:"); exp.printStackTrace (out); out.println (); if (exp instanceof ChainException) { ChainException ee = (ChainException) exp; ee.printInnerExceptionChain (out); } else { exp.printStackTrace (out); } } } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/CollectionsUtils.java000066400000000000000000000120461157743670400265450ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.Map; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Arrays; import java.lang.reflect.InvocationTargetException; public class CollectionsUtils { /** * Given a Map return the entries in the Map in the passed in * List in order. * * @param map The Map to use. * @param list The List to fill up. */ public static void getMapEntriesAsOrderedList (Map map, List list) { // Get all the keys from the Map as an Array. Object[] keys = map.keySet ().toArray (); // Do a natural sort. Arrays.sort (keys); // Cycle over them and pull out the associated value // from the Map placing it into the List. for (int i = 0; i < keys.length; i++) { list.add (map.get (keys[i])); } } /** * Get all the entries in the Map object out and place into * the List passed in, just add at the bottom. * * @param map The Map to get objects out of. * @param list The List to add objects to the end of. We add the * Map.Entry. */ public static void addMapEntriesToList (Map map, List list) { Iterator iter = map.entrySet ().iterator (); while (iter.hasNext ()) { list.add (((Map.Entry) iter.next ()).getValue ()); } } public static List convertToRows (List objs, int rowSize) { List retData = new ArrayList (); if ((rowSize == 0) || (rowSize >= objs.size ()) ) { retData.add (objs); return retData; } int count = 0; List row = new ArrayList (); for (int i = 0; i < objs.size (); i++) { row.add (objs.get (i)); count++; if (count == rowSize) { // We need a new row, add this row to our return data. retData.add (row); row = new ArrayList (); count = 0; } } while (count != rowSize) { row.add (null); count++; } retData.add (row); return retData; } /** * Given a List of objects, convert the values it contains to the passed in Map. * The keyAccessor and valueAccessor parameters should * be {@link Getter accessors} and specify the key and value for the * map respectively. Set valueAccessor to null to mean that * the object from the List should be added as the value. Note: all the objects * in the list MUST be of the same type, to determine the class the first object * in the list is examined. * * @param objs The objects to convert. * @param map The Map to add the objects to. * @param keyAccessor The accessor for the key to set in the map. * @param valueAccessor The accessor for the value to set in the map, set to null * to get the object added. */ public static void convertListToMap (List objs, Map map, String keyAccessor, String valueAccessor) throws IllegalAccessException, InvocationTargetException, ClassCastException { if ((objs == null) || (map == null) ) { return; } if (objs.size () == 0) { return; } Class c = objs.get (0).getClass (); Getter ka = new Getter (keyAccessor, c); Getter va = null; if (valueAccessor != null) { va = new Getter (valueAccessor, c); } for (int i = 0; i < objs.size (); i++) { Object o = objs.get (i); Object k = ka.getValue (o); Object v = null; if (va == null) { v = o; } else { v = va.getValue (o); } map.put (k, v); } } /** * Add all the entries in the List as keys in the specified map. * Set "keyIsValue" to true to put the entry from * the List as the value for the map as well, if it's set to * false then we use "". * * @param list The List to get entries from. * @param map The Map to add entries to. * @param keyIsValue Indicate whether we should use the List entry * as the value as well or "". */ public static void addListEntriesToMap (List list, Map map, boolean keyIsValue) { for (int i = 0; i < list.size (); i++) { if (keyIsValue) { map.put (list.get (i), list.get (i)); } else { map.put (list.get (i), ""); } } } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/DynamicGetter.java000066400000000000000000000061051157743670400260040ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.ArrayList; import java.util.StringTokenizer; /** * This class is used to perform access into a Java object using a * String value with a specific notation. This class differs from the {@link Getter} * class in that instead of creating the chain of methods when the getter is * instantiated it will instead get the actual method from the object passed in. */ public class DynamicGetter { private List chain = new ArrayList (); private Class clazz = null; private Object origObj = null; private Object origObjRes = null; private int cs = 0; /** * Get the getter associated with the named reference. Return * null if there isn't one, or if we can't access it. * * @param ref The reference for the getter. * @param obj The Object to build up the getter from. */ public DynamicGetter (String ref, Object obj) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { this.clazz = obj.getClass (); this.origObj = obj; StringTokenizer t = new StringTokenizer (ref, "."); Class c = obj.getClass (); while (t.hasMoreTokens ()) { String tok = t.nextToken (); // Create a Getter for this. Getter g = new Getter (tok, c); this.chain.add (g); c = g.getType (); } this.cs = this.chain.size (); } public Class getBaseClass () { return this.clazz; } /** * Get the class of the type of object we would return from the {@link #getValue(Object)} * method. * * @return The class. */ public Class getType () { return ((Getter) this.chain.get (this.cs - 1)).getType (); } public Object getValue (Object obj) throws IllegalAccessException, InvocationTargetException { // If the object is null then return null. if (obj == null) { return null; } if (this.origObj == obj) { return this.origObjRes; } // For our accessor chain, use the Field and Methods // to get the actual value. Object retdata = obj; for (int i = 0; i < this.cs; i++) { Getter g = (Getter) this.chain.get (i); retdata = g.getValue (retdata); } return retdata; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/FileChangeEvent.java000066400000000000000000000037331157743670400262400ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.io.File; import java.util.EventObject; public class FileChangeEvent extends EventObject { public static final int EXISTS = 1; public static final int NOT_EXISTS = 2; public static final int MODIFIED = 4; public static final int HIDDEN = 8; public static final int NOT_HIDDEN = 16; public static final int LENGTH_CHANGED = 32; public static final int READABLE = 64; public static final int NOT_READABLE = 128; public static final int WRITEABLE = 256; public static final int NOT_WRITEABLE = 512; public static final int FILE_TYPE_CHANGE = 1024; private FileDetails newD = null; private FileDetails oldD = null; private int types = -1; public FileChangeEvent (File file, FileDetails newDetails, FileDetails oldDetails, int eventTypes) { super (file); this.newD = newDetails; this.oldD = oldDetails; this.types = eventTypes; } public boolean hasEvent (int type) { if ((this.types & type) > 0) { return true; } return false; } public int getEventTypes () { return this.types; } public FileDetails getOldFileDetails () { return this.oldD; } public FileDetails getNewFileDetails () { return this.newD; } public File getFile () { return (File) this.getSource (); } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/FileChangeListener.java000066400000000000000000000014451157743670400267420ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.EventListener; public interface FileChangeListener extends EventListener { public void fileChanged (FileChangeEvent e, int types); } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/FileDetails.java000066400000000000000000000042771157743670400254420ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.io.File; import java.io.IOException; public class FileDetails { private boolean exists = false; private boolean readable = false; private boolean writeable = false; private long length = 0; private boolean hidden = false; private boolean file = false; private long lastModified = 0; private String name = null; private String path = null; private File f = null; public FileDetails (File file) { if (file.exists ()) { this.exists = true; this.length = file.length (); this.lastModified = file.lastModified (); this.name = file.getName (); this.path = file.getPath (); if (file.isFile ()) { this.file = true; } if (file.isHidden ()) { this.hidden = true; } if (file.canWrite ()) { this.writeable = true; } if (file.canRead ()) { this.readable = true; } } } public File getFile () { return this.f; } public boolean isDirectory () { return !this.file; } public boolean isFile () { return this.file; } public boolean canWrite () { return this.writeable; } public boolean canRead () { return this.readable; } public long getLength () { return this.length; } public boolean isHidden () { return this.hidden; } public long lastModified () { return this.lastModified; } public boolean exists () { return this.exists; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/FileWatcher.java000066400000000000000000000255001157743670400254420ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.Iterator; import java.io.File; import java.io.FileFilter; import com.gentlyweb.utils.FileChangeEvent; import com.gentlyweb.utils.FileChangeListener; import com.gentlyweb.utils.FileDetails; public class FileWatcher { private Map files = new HashMap (); private Worker w = null; public FileWatcher () { this.w = new Worker (this); } /** * Add a file to be watched. If the file maps to a directory * then we do nothing. Note: if you have already started the * thread that contains this FileWatcher then the file will only * be watched on the next "watch cycle". That is it may be longer * than "sleepTime" before any changes to the file are noticed and * reported. If we already have this file then we do nothing, in that * case changes may be reported before "sleepTime" has elapsed. * * @param f The file to add. */ public void addFile (File f) { if (!this.files.containsKey (f)) { this.files.put (f, new FileDetails (f)); } } /** * Stop a file being watched. Note: if you have already started the * thread that contains this FileWatcher then any existing changes to the * file may still be reported since the watcher could be currently processing * (or be about to process) the file. * * @param f The file to remove. */ public void removeFile (File f) { this.files.remove (f); } /** * Stop ALL files from being watched. Note: if you have already started the * thread that contains this FileWatcher then any existing changes to the files * may still be reported since the watcher could be currently processing * one or more of the files. After the watchers current watch cycle then the * files are guaranteed to no longer be watched. */ public void clearAll () { this.files.clear (); } /** * Add all the files in the specified directory. Use the FileFilter to provide * filtering of files that we don't want to add. If the filter is null then * we just add all the files. If dir points to a file then we do nothing. * * @param dir The directory. * @param ff The FileFilter to use to determine which files to accept/reject. */ public void addAll (File dir, FileFilter ff) { // See if the dir is really a directory. if (!dir.isDirectory ()) { return; } if (!dir.canRead ()) { return; } File files[] = dir.listFiles (); for (int i = 0; i < files.length; i++) { if (this.files.containsKey (files[i])) { // Goto the next... continue; } if (ff == null) { this.files.put (files[i], new FileDetails (files[i])); } else { if (ff.accept (files[i])) { this.files.put (files[i], new FileDetails (files[i])); } } } } /** * Remove all the files that we are watching that have the specified directory * as given by the passed in File. If the passed in File is not a directory then * we do nothing. To perform the check we call: File.getParentFile () on each file * and then do dir.equals (parentFile) to see if they are equal, if they are * then we remove the file. * * @param dir The parent directory of files that we want to remove. */ public synchronized void removeAll (File dir) { if (!dir.isDirectory ()) { return; } if (!dir.canRead ()) { return; } Map newFs = new HashMap (); Iterator iter = this.files.keySet ().iterator (); while (iter.hasNext ()) { File f = (File) iter.next (); File pFile = f.getParentFile (); if (!pFile.equals (dir)) { newFs.put (f, this.files.get (f)); } } } /** * Remove all the files from our list of watched files according to the * FileFilter passed in. This method is synchronized to prevent concurrent * modification issues. * * @param ff The FileFilter to determine what should be removed. */ public synchronized void removeAll (FileFilter ff) { Map newFs = new HashMap (); Iterator iter = this.files.keySet ().iterator (); while (iter.hasNext ()) { File f = (File) iter.next (); // If the file filter rejects the file then we should keep it! if (!ff.accept (f)) { newFs.put (f, this.files.get (f)); } } this.files = newFs; } protected synchronized Map getFiles () { Map fs = new HashMap (); fs.putAll (this.files); return fs; } protected synchronized void setFileDetails (File f, FileDetails fd) { this.files.put (f, fd); } public void setCheckRepeatTime (long millis) { this.w.setCheckRepeatTime (millis); } public void start () { this.w.start (); } public void stop () { this.w.stop (); } protected void fireFileChange (File file, FileChangeEvent event, int types) { this.w.fireFileChange (file, event, types); } public void removeFileChangeListener (FileChangeListener f) { this.w.removeFileChangeListener (f); } public void addFileChangeListener (FileChangeListener f, int changeTypes) { this.w.addFileChangeListener (f, changeTypes); } public void addFileChangeListener (FileChangeListener f) { this.w.addFileChangeListener (f); } private class FileListener { private int types = -1; private FileChangeListener listener = null; private FileListener (FileChangeListener l, int types) { this.listener = l; this.types = types; } private boolean hasEvents (int types) { if (this.types < 0) { return true; } if ((this.types & types) > 0) { return true; } return false; } } protected class Worker implements Runnable { private boolean shouldRun = false; private List listeners = new ArrayList (); private long sleepTime = 5000; private FileWatcher fw = null; public Worker (FileWatcher fw) { this.fw = fw; } public void run () { while (this.shouldRun) { Map files = this.fw.getFiles (); Iterator iter = files.keySet ().iterator (); while (iter.hasNext ()) { File f = (File) iter.next (); FileDetails fd = (FileDetails) files.get (f); this.processFile (f, fd); } if (this.shouldRun) { try { Thread.sleep (this.sleepTime); } catch (Exception e) { // Not sure what we can do here... } } } } private void processFile (File f, FileDetails det) { int types = 0; // Work out if it's changed... if (f.exists () != det.exists ()) { if (f.exists ()) { types += FileChangeEvent.EXISTS; } else { types += FileChangeEvent.NOT_EXISTS; } } else { if (f.exists ()) { if (f.lastModified () != det.lastModified ()) { types += FileChangeEvent.MODIFIED; } if (f.isHidden () != det.isHidden ()) { if (f.isHidden ()) { types += FileChangeEvent.HIDDEN; } else { types += FileChangeEvent.NOT_HIDDEN; } } if (f.length () != det.getLength ()) { types += FileChangeEvent.LENGTH_CHANGED; } if (f.canRead () != det.canRead ()) { if (f.canRead ()) { types += FileChangeEvent.READABLE; } else { types += FileChangeEvent.NOT_READABLE; } } if (f.canWrite () != det.canWrite ()) { if (f.canWrite ()) { types += FileChangeEvent.WRITEABLE; } else { types += FileChangeEvent.NOT_WRITEABLE; } } if (f.isFile () != det.isFile ()) { types += FileChangeEvent.FILE_TYPE_CHANGE; } } } if (types > 0) { if (this.shouldRun) { FileDetails newDetails = new FileDetails (f); FileChangeEvent e = new FileChangeEvent (f, newDetails, det, types); this.fireFileChange (f, e, types); this.fw.setFileDetails (f, newDetails); } } } public void setCheckRepeatTime (long millis) { this.sleepTime = millis; } public void start () { if (!this.shouldRun) { this.shouldRun = true; Thread t = new Thread (this); t.setDaemon (true); t.start (); } } public void stop () { this.shouldRun = false; } protected void fireFileChange (File file, FileChangeEvent event, int types) { for (int i = 0; i < this.listeners.size (); i++) { FileListener l = (FileListener) this.listeners.get (i); if (l.hasEvents (types)) { l.listener.fileChanged (event, types); } } } public void removeFileChangeListener (FileChangeListener f) { for (int i = 0; i < this.listeners.size (); i++) { FileListener fl = (FileListener) this.listeners.get (i); if (fl.listener == f) { this.listeners.remove (i); return; } } } public void addFileChangeListener (FileChangeListener f, int changeTypes) { // Ensure that we don't already have the listener. for (int i = 0; i < this.listeners.size (); i++) { if (this.listeners.get (i) == f) { return; } } this.listeners.add (new FileListener (f, changeTypes)); } public void addFileChangeListener (FileChangeListener f) { for (int i = 0; i < this.listeners.size (); i++) { if (this.listeners.get (i) == f) { return; } } this.listeners.add (new FileListener (f, -1)); } } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/FilterException.java000066400000000000000000000004241157743670400263470ustar00rootroot00000000000000package com.gentlyweb.utils; public class FilterException extends ChainException { public FilterException (String message, Exception e) { super (message, e); } public FilterException (String message) { super (message); } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/GZIPFileDataSource.java000066400000000000000000000211371157743670400265730ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.io.File; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import javax.activation.DataSource; /** * A Class for handling a gzipped file as a DataSource. *

*

Description Contents

* *

Outline

* This class providers a wrapper around a file such that GZIPed files will * be unzipped when the {@link #getInputStream()} method is called. Or * that the file will be zipped with the named zip file extension when * the {@link #getOutputStream()} method is called. *

*

Zip file extension

*

* When used as an output this class expects your file to be gzipped * and be able to be unzipped using a GZIPInputStream. *

* When used an an input we use a GZIPOutputStream to write the file * in a gzipped form. *

* For the content type we only support a number of different content types * and this is based upon the file extension without any zip file extension. * What this means is that if you have a File called: *

*
 *   myFile.txt.gz
 * 
*

* And you call the {@link #getName()} method then the name: *

*
 *   myFile.txt
 * 
*

* Will be returned and the content type would be: text/plain. *

* If you don't specify a zipExtension then a default of .gz is * used. *

*

Content Types

*

* The file extensions we support and their content types are listed below * (in other words does the file name end with the value specified): *

* * * * * * * * * * * * * * * * * * * * * * * * *
File ExtensionContent Type Returned
.docapplication/msword
.sdwapplication/x-swriter
.rtftext/rtf
.htmltext/html
.txttext/plain
*

* It should be noted that file extensions are case-insensitive, this is because * on Windows platforms .doc and .DOC are treated the same. *

* If you know of any other content types/file extensions that MUST * be supported directly by this class then please contact code-monkey@gentlyweb.com * with details. *

*
Default content type
*

* If the file extension does not match any of the "pre-defined" file extensions * then a content type of application/octet-stream is returned. *

*

Stream Buffering

*

* Both the input and output streams that you gain from this class are * buffered. *

*

Thread safety and reuse

*

* It is possible to reuse this class by setting a new File via the * {@link #setFile(File)} method and potentially a different zip extension * via the {@link #setZipExtension(String)}. *

* This class is NOT Thread safe and you should synchronize externally if * you plan to use multiple Threads with it. *

*/ public class GZIPFileDataSource implements DataSource { private File file = null; private String zipExtension = ".gz"; /** * Create a new data source for the specified file. *

* We expect the file to have the file extension as given by zipExtension. *

* * @param f The File. * @param zipExtension The file extension for gzipped files. Set to null * to use the default of .gz. */ public GZIPFileDataSource (File f, String zipExtension) { this.file = f; if (zipExtension != null) { this.zipExtension = zipExtension.toLowerCase (); } } /** * Get the content type for this data source. *

* We base the content type on the file extension of the file minus the * zipExtension, so if a file is called myFile.txt.gz and * the zip extension is .gz then we trim off the .gz and * then look for the "real" file extension, then determine the * appropriate content type and return it. *

* You should note that the file DOESN'T have to have the * zipExtension for this method to work. *

* If we don't have a specific file extension to use (see the * table of content type to file extension mappings for full details * of what this method returns. * * @return The content type based upon the file extension of the file, or * application/octet-stream if we don't recognise the file extension. */ public String getContentType () { String fName = this.file.getName ().toLowerCase (); // Strip off any gz file extension... if (fName.indexOf (this.zipExtension) != -1) { fName = fName.substring (0, fName.indexOf (this.zipExtension)); } if (fName.endsWith (".doc")) { return "application/msword"; } if (fName.endsWith (".sdw")) { return "application/x-swriter"; } if (fName.endsWith (".rtf")) { return "text/rtf"; } if (fName.endsWith (".html")) { return "text/html"; } if (fName.endsWith (".txt")) { return "text/plain"; } return "application/octet-stream"; } /** * Get an appropriate InputStream for the data source. *

* Here we just return a buffered GZIPInputStream. * * @return The InputStream for the data source. * @throws IOException If we can't get the stream to the source. */ public InputStream getInputStream () throws IOException { return new GZIPInputStream (new BufferedInputStream (new FileInputStream (this.file))); } /** * Get an appropriate OutputStream for the data source. *

* Here we just return a buffered GZIPOutputStream. * * @return The OutputStream for the data source. * @throws IOException If we can't get the stream to the source. */ public OutputStream getOutputStream () throws IOException { return new GZIPOutputStream (new BufferedOutputStream (new FileOutputStream (this.file))); } /** * Set the zip extension to use. * * @param ext The zip extension. */ public void setZipExtension (String ext) { this.zipExtension = ext; } /** * Set the File to use for the data source. * * @param file The file. */ public void setFile (File file) { this.file = file; } /** * Get the name of the data source. *

* If the file ends with the zipExtension then we strip that off * before returning the name. * * @return The name of the data source. */ public String getName () { String fName = this.file.getName ().toLowerCase (); // Strip off any gz file extension... if (fName.indexOf (this.zipExtension) != -1) { fName = fName.substring (0, fName.indexOf (this.zipExtension)); } return fName; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/GeneralCollector.java000066400000000000000000000205771157743670400265020ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.List; import java.util.Map; import java.util.Collection; import java.util.Iterator; import java.util.Map.Entry; import java.lang.reflect.InvocationTargetException; public class GeneralCollector { public static final String KEYS = "KEYS"; public static final String VALUES = "VALUES"; private Class clazz = null; private String field = null; private List accessorChain = null; public GeneralCollector (Class c) { this.clazz = c; } public GeneralCollector (Class c, String field) { this (c); this.setField (field); } public void collect (Map objects, String type, List retData) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Iterator iter = objects.entrySet ().iterator (); while (iter.hasNext ()) { Map.Entry obj = (Map.Entry) iter.next (); Object key = obj.getKey (); Object value = obj.getValue (); if (type.equals (GeneralCollector.KEYS)) { if (!key.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected key object to be of type: " + this.clazz.getName () + ", got: " + key.getClass ().getName ()); } retData.add (Accessor.getValueFromAccessorChain (key, this.accessorChain)); } else { if (!value.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected value object to be of type: " + this.clazz.getName () + ", got: " + value.getClass ().getName ()); } retData.add (Accessor.getValueFromAccessorChain (value, this.accessorChain)); } } } public void collect (Map objects, String type, Collection retData) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Iterator iter = objects.entrySet ().iterator (); while (iter.hasNext ()) { Map.Entry obj = (Map.Entry) iter.next (); Object key = obj.getKey (); Object value = obj.getValue (); if (type.equals (GeneralCollector.KEYS)) { if (!key.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected key object to be of type: " + this.clazz.getName () + ", got: " + key.getClass ().getName ()); } retData.add (Accessor.getValueFromAccessorChain (key, this.accessorChain)); } else { if (!value.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected value object to be of type: " + this.clazz.getName () + ", got: " + value.getClass ().getName ()); } retData.add (Accessor.getValueFromAccessorChain (value, this.accessorChain)); } } } public void collect (Map objects, String type, Map retData) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Iterator iter = objects.entrySet ().iterator (); while (iter.hasNext ()) { Map.Entry obj = (Map.Entry) iter.next (); Object key = obj.getKey (); Object value = obj.getValue (); if (type.equals (GeneralCollector.KEYS)) { if (!key.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected key object to be of type: " + this.clazz.getName () + ", got: " + key.getClass ().getName ()); } retData.put (Accessor.getValueFromAccessorChain (key, this.accessorChain), value); } else { if (!value.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected value object to be of type: " + this.clazz.getName () + ", got: " + value.getClass ().getName ()); } retData.put (key, Accessor.getValueFromAccessorChain (value, this.accessorChain)); } } } public void collect (Collection objects, Collection retData) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Iterator iter = objects.iterator (); while (iter.hasNext ()) { Object o = iter.next (); if (!o.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected object to be of type: " + this.clazz.getName () + ", got: " + o.getClass ().getName ()); } // Apply the fields... retData.add (Accessor.getValueFromAccessorChain (o, this.accessorChain)); } } public void collect (Collection objects, List retData) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Iterator iter = objects.iterator (); while (iter.hasNext ()) { Object o = iter.next (); if (!o.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected object to be of type: " + this.clazz.getName () + ", got: " + o.getClass ().getName ()); } // Apply the fields... retData.add (Accessor.getValueFromAccessorChain (o, this.accessorChain)); } } public void collect (List objects, List retData) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { for (int i = 0; i < objects.size (); i++) { Object o = objects.get (i); if (!o.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected object at index: " + i + " to be of type: " + this.clazz.getName () + ", got: " + o.getClass ().getName ()); } // Apply the fields... retData.add (Accessor.getValueFromAccessorChain (o, this.accessorChain)); } } public void collect (List objects, Collection retData) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { for (int i = 0; i < objects.size (); i++) { Object o = objects.get (i); if (!o.getClass ().isAssignableFrom (this.clazz)) { throw new IllegalArgumentException ("Expected object at index: " + i + " to be of type: " + this.clazz.getName () + ", got: " + o.getClass ().getName ()); } // Apply the fields... retData.add (Accessor.getValueFromAccessorChain (o, this.accessorChain)); } } /** * Set the field that we collect if you readd the same field then * the type is just updated. * * @param field The field to sort on. * @throws IllegalArgumentException If we can't find the field in the * class/class chain passed into the constructor. */ public void setField (String field) throws IllegalArgumentException { // Find the field... this.field = field; this.accessorChain = Accessor.getAccessorChain (field, this.clazz); } /** * Get a field given a field name. * * @return The field or null if the field hasn't been set yet. */ public String getField () { return this.field; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/GeneralComparator.java000066400000000000000000000644261157743670400266640ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.util.Map; import java.util.List; import java.util.ArrayList; import java.util.Comparator; import java.util.StringTokenizer; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Attribute; //import com.gentlyweb.xml.JDOMUtils; //import com.gentlyweb.xml.JDOMXmlOutputter; /** * A general purpose comparator designed to compare any pair of * heterogeneous objects. *

* Skip to bottom of description. *

*

Description Contents:

* *

Basic Operation

*

* This class works by you specify a number of fields that are then * used to access into each of the objects. The term field is a bit mis-leading * since you can specify either a field name within a class or a zero argument * method name. *

* When specifying a field you can use dot notation to acess into return types/ * field types. *

* For instance if you have the following class structure: *

*
 * public class A 
 * {
 *    public B = new B ();
 * }
 * 
 * public class B
 * {
 *    public C = new C ();
 * }
 * 
 * public class C
 * {
 *    String d = "";
 * }
 * 
*

* Then to perform comparison between between 2 objects of type A on * the d field in Class C you would specify a field of: B.C.d. * Notice that you don't specify the current class, all fields are taken relative * to the current type. *

* Similarly, if class C actually looked like: *

*
 * public class C
 * {
 *    private String d = "";
 *
 *    public String getD ()
 *    {
 *       return d;
 *    }   
 * } 
 * 
*

* Then you would could use the same field since if a public field with the * specified name cannot be found then it is converted to a method name instead, * following JavaBeans conventions. So the d is converted to getD * and a public method is looked for (with no arguments) with that name. Finally if a * get* method cannot be found then a method with just the name, in this case d, * is searched for that takes no arguments. (Note: this has been added because there are a * number of cases where the standard java.*.* classes DO NOT follow the JavaBeans conventions * but you would want to use a GeneralComparator). *

* You can override the method name conversion by using the actual method name * (in case your method doesn't follow the JavaBeans convention), so if C * looked like: *

*
 * public class C
 * {
 *    private d = "";
 *    
 *    public String getMyDField ()
 *    {
 *       return d;
 *    }
 * }
 * 
*

* You would then use a field of: B.C.getMyDField in which case the * method getMyDField would be looked for instead of looking for * field. *

*

Ascending/Descending Comparison

*

* When you add a field you specify whether you want the comparison to * occur in ascending or descending order. Setting to have a * descending search just means that the result gained from * comparing values is reversed. *

*

Comparing values

*

* If a field value equates to a public Java field in the object then * we call: java.lang.reflect.Field.get(Object) on each * of the objects passed in and then if the type of the field implements * Comparable then we call: Comparable.compareTo(Object,Object) * to get the appropriate value. If the type of the field does NOT * implement Comparable then we call toString on the object * returned and then call String.compareTo(Object,Object) for * the value to return instead. *

* Similarly we do the same when the field value equates to a public Java method * of the object. We call java.lang.reflect.Method.invoke(Object,Object[]) * with a zero-length argument list and then if the return type of the method * implements Comparable we call Comparable.compareTo(Object,Object) * with the result of the method calls to get our value. Otherwise we call * toString on the returned values and then call String.compareTo(Object,Object) * to get the value. *

* The upshot of all this is that you can sort a list of objects on values * returned from method calls or by fields that may be X levels deep within * an Object WITHOUT having to implement a complex version of the Comparable * interface yourself. *

*

Multi field sorting

*

* You can specify as many fields as you want to compare on, when the comparison * is made (via the {@ #compare(Object,Object)} method) we only compare on * as many fields as we need to, so if the result of the first field comparison * indicates that the values are different we just return since there is no * point doing further comparing, other fields will have no effect. Only * if the values are equal do we move onto "lower" fields. *

*

Thread safety and reuse

*

* This class is NOT Thread safe and never will be (or should be...) * since you would not want other threads modifying the fields you compare on. * Once configured however the comparator is perfectly reusable since the * only data it holds relates to the fields/methods that are accessed. If you * do plan to use across multiple Threads then you need some kind of external * synchronization, for example: *

*
 * public synchronized void sortObjects (List objs)
 * {
 *
 *     Collections.sort (objs,
 *                       myGeneralComparator);
 *
 * }
 * 
*

*

Efficiency

*

* To try and access into the Object structure we use a {@link Getter} * which is basically a List of Field and Methods that should be * accessed/invoked when trying to find the value we require. Since we * are reliant on reflection for this the key factor is the cost of * traversing down the accessor chain (so the size of it will be important). * The accessor chain is gained when you call one of the add*** methods * so the finding of the method/field is a one-off. *

*

Warning on Getters

*

* The Getter class supports the [ ] notation for accessing * Maps and Lists however in terms of comparisons and sorting this means * little and should NOT be used!!! This is not checked because * there may be situations where you would want to do it, however you would * need to ensure that your Lists/Maps contain heterogeneous Objects. *

*

JDOM Support

*

* This class is capable of initing itself from a JDOM Element * and also to save it's current state into a JDOM element. This is * primarily to support the {@link ConfigList} and {@link ConfigMap} * objects, however it can be used in other places when you may want * to keep the state of the comparator. *

*

Format

*

* When {@link #getAsJDOMElement()} is called it will return a JDOM Element * in the form given below (conversely, the constructor {@link #GeneralComparator(Element)} * expects the passed in JDOM element to have the same format): *

 *   &#lt;comparator class="[[NAME OF CLASS THAT IS BEING COMPARED]]">
 *    &#lt;field id="[[ACCESSOR VALUE INTO THE OBJECT]]"
 *               type="either ASC or DESC" />
 *    &#lt;!-- 
 *     There can be X number of fields, the minimum is 1.
 *    -->
 *   &#lt;/>
 * 
*

*

Examples

*

* Say we wanted to sort a number of {@link Property} objects. *

*
 * // Create a new GeneralComparator.
 * GeneralComparator g = new GeneralComparator ({@link Property}.class);
 * 
 * // Now we want to sort them on type first, then id then description, then
 * // value.
 * g.{@link addField(String,String) addField} ("type",
 *             GeneralComparator.ASC);
 * g.{@link addField(String,String) addField} ("getID",
 *             GeneralComparator.ASC);
 * g.{@link addField(String,String) addField} ("description",
 *             GeneralComparator.ASC);
 * g.{@link addField(String,String) addField} ("value",
 *             GeneralComparator.ASC);
 *
 * // Then (by magic...) get our collection of Properties that can
 * // be sorted.
 * List properties = Helper.getProperties ();
 *
 * // Now sort them using our comparator.
 * Collections.sort (properties,
 *                   g);
 * 
*

* We could always set any of the fields to be a descending sort. Notice * here that we use "getID" since that method name is not using the * correct JavaBeans convention. Also, {@link Property} implements the * Comparable interface but using the comparator will * override it. *

* Or if we wanted to sort a slice of messages from a {@link Logger}: *

*
 * GeneralComparator g = new GeneralComparator (Logger.Message.class);
 * 
 * // Sort on the time, but this time sort on the day of the Date
 * // this is a deprecated method but it's not a biggie.  We want
 * // them in reverse order.
 * g.addField ("time.day",
 *             GeneralComparator.DESC);
 *
 * // Get our slice...
 * long time = System.currentTimeMillis ();
 * Date now = new Date (time);
 * Date oneDayAgo = new Date (time - (24*60*60*1000));
 * int types = Logger.Message.INFORMATION || Logger.Message.ERROR;
 * List messages = myLogger.getMessages (now,
 *                                       oneDayAgo,
 *                                       types);
 * 
 * Collections.sort (messages,
 *                   g);
 *
 * // We could perform another sort but this time, sorting first
 * // on the type, this should be in ascending order, i.e. ERROR first...
 * g.addFieldBefore ("type",
 *                   GeneralComparator.ASC,
 *                   "time.day");
 *
 * Collections.sort (messages,
 *                   g);
 * 
*

* A quite common need is to sort the entries in a Map rather than the keys but * still maintain the key/value relationship. To do so use the code below: *

 * // Get all the entries in the Map as a List.
 * List l = new ArrayList ();
 * l.addAll (myMap.entrySet ());
 *
 * // Create a GeneralComparator.  It is also possible to use the specific
 * // implementation of the Map.Entry interface for the specific Map but
 * // this way keeps it generic.
 * GeneralCompartor gc = new GeneralComparator (Map.Entry.class);
 *
 * // Specify the "value" (i.e. getValue method).
 * gc.addField ("value",
 *              GeneralComparator.DESC);
 *  
 * // Sort.
 * Collections.sort (l,
 *                   gc);
 * 
*

* Back to top of description. *

*/ public class GeneralComparator implements Comparator, Serializable//, //JDOMXmlOutputter { public class XMLConstants { public static final String root = "comparator"; public static final String clazz = "class"; public static final String field = "field"; public static final String id = "id"; public static final String type = "type"; } public int count = 0; /** * Use to indicate that a field should be sorted in ascending order. */ public static final String ASC = "ASC"; /** * Use to indicate that a field should be sorted in descending order. */ public static final String DESC = "DESC"; private List fields = new ArrayList (); private Class clazz = null; /** * Create a new GeneralComparator using the data held in the JDOM * element. * * @param root The root JDOM element. * @throws JDOMException If the format is incorrect. * @throws ChainException If we can't load the class that we need. * @throws IllegalArgumentException If the field is invalid. */ /* public GeneralComparator (Element root) throws JDOMException, ChainException, IllegalArgumentException { JDOMUtils.checkName (root, GeneralComparator.XMLConstants.root, true); // Get the class. String clazz = JDOMUtils.getAttributeValue (root, GeneralComparator.XMLConstants.clazz); try { this.clazz = Class.forName (clazz); } catch (Exception e) { throw new ChainException ("Unable to load class: " + clazz, e); } // Get all the fields, there should be at least 1. List fEls = JDOMUtils.getChildElements (root, GeneralComparator.XMLConstants.field, true); for (int i = 0; i < fEls.size (); i++) { Element e = (Element) fEls.get (i); // Get the id attribute. String id = JDOMUtils.getAttributeValue (e, GeneralComparator.XMLConstants.id); // Get the type attribute. String type = JDOMUtils.getAttributeValue (e, GeneralComparator.XMLConstants.type); if ((!type.equals (GeneralComparator.ASC)) && (!type.equals (GeneralComparator.DESC)) ) { Attribute attr = JDOMUtils.getAttribute (e, GeneralComparator.XMLConstants.type, true); throw new JDOMException ("Type (path: " + JDOMUtils.getPath (attr) + ") must be either: " + GeneralComparator.ASC + " or: " + GeneralComparator.DESC); } // Add the field. this.addField (id, type); } } */ public GeneralComparator (Class c) { this.clazz = c; } public Class getCompareClass () { return this.clazz; } public void addField (Getter field, String type) throws IllegalArgumentException { // Get the class that the getter relates to, they MUST be the same class AND the // same object... classes loaded via other classloaders are not compatible. if (field.getBaseClass ().hashCode () != this.clazz.hashCode ()) { throw new IllegalArgumentException ("Class in Getter is: " + field.getBaseClass ().getName () + " with hashCode: " + field.getBaseClass ().hashCode () + " which is incompatible with comparator class: " + this.clazz.getName () + " with hashCode: " + this.clazz.hashCode ()); } if (this.fields.size () == 0) { this.fields.add (0, new SortField (field, type, this.clazz)); return; } this.fields.add (this.fields.size (), new SortField (field, type, this.clazz)); } /** * Add a new field in at the specified index. Remember that indices * start at 0 and proceed in asceding order. If the index specified * is <0 then we add the field in at 0, moving everything else down * by 1. If the index specified is >(fields.length - 1) then we just * add to the end of the fields. * * @param field The field to add. * @param type The type, either GeneralComparator.ASC or GeneralComparator.DESC. * @param index The index to add at. * @throws IllegalArgumentException If we can't find the field in the * class/class chain passed into the constructor. */ public void addFieldAtIndex (String field, String type, int index) throws IllegalArgumentException { if (index < 0) { this.fields.add (0, new SortField (field, type, this.clazz)); return; } if (index > (this.fields.size () - 1)) { this.fields.add (new SortField (field, type, this.clazz)); return; } this.fields.add (index, new SortField (field, type, this.clazz)); } /** * Add a new field in BEFORE the named field, if we don't have the * named field then we just call {@link #addField(String,String)} which * will add the field in after all the others. * * @param field The field to add. * @param type Sort either ascending or descending, should be either * GeneralComparator.ASC or GeneralComparator.DESC. * @param ref The reference field. * @throws IllegalArgumentException If we can't find the field in the * class/class chain passed into the constructor. */ public void addFieldBefore (String field, String type, String ref) throws IllegalArgumentException { // Get the field index... int fi = this.getFieldIndex (ref); if (fi != -1) { this.addFieldAtIndex (field, type, --fi); } else { this.addField (field, type); } } /** * Add a new field in AFTER the named field, if we don't have the * named field then we just call {@link #addField(String,String)} which * will add the field in after all the others. * * @param field The field to add. * @param type Sort either ascending or descending, should be either * GeneralComparator.ASC or GeneralComparator.DESC. * @param ref The reference field. * @throws IllegalArgumentException If we can't find the field in the * class/class chain passed into the constructor. */ public void addFieldAfter (String field, String type, String ref) throws IllegalArgumentException { // Get the field index... int fi = this.getFieldIndex (ref); if (fi != -1) { this.addFieldAtIndex (field, type, ++fi); } else { this.addField (field, type); } } /** * Remove a field that we sort on. If we don't have the field then * we do nothing. * * @param field The field to remove. */ public void removeField (String field) { SortField f = this.getField (field); if (f != null) { this.fields.remove (f); } } /** * Add a field that we sort on, if you readd the same field then * the type is just updated. The order in which you add the fields * provides the order in which the objects are sorted. * * @param field The field to sort on. * @param type The type either GeneralComparator.ASC or GeneralComparator.DESC. * @throws IllegalArgumentException If we can't find the field in the * class/class chain passed into the constructor. */ public void addField (String field, String type) throws IllegalArgumentException { // Find the field... SortField f = this.getField (field); if (f != null) { f.type = type; return; } this.addFieldAtIndex (field, type, (this.fields.size () + 1)); } /** * Get the index of this field. * * @param field The field to look for. * @return The index (0 or greater) or -1 if we don't have the field. */ private int getFieldIndex (String field) { for (int i = 0; i < this.fields.size (); i++) { SortField f = (SortField) this.fields.get (i); if (f.field.equals (field)) { return i; } } return -1; } /** * Get a field given a field name. * * @param field The name of the field. * @return The SortField object or null if we don't have it. */ private SortField getField (String field) { for (int i = 0; i < this.fields.size (); i++) { SortField f = (SortField) this.fields.get (i); if (f.field.equals (field)) { return f; } } return null; } /** * Implement the {@link Comparator.compare(Object,Object)} method. * Here we check each field in turn, we only check subsequent fields if * the "higher" up fields are equal. So if fields 0 and 1 are both equal * then we check field 2 and so on... Note: it is possible that we have * an exception thrown here, however the compare method doesn't allow * for exceptions to be thrown, so we just consume them and return 0, we * only catch IllegalAccessException and InvocationTargetException. * * @param obj1 The first object. * @param obj2 The second object. * @return A value according to the rules laid out in {@link Comparator.compare(Object,Object)}, * if either object is null then we return 0 or we return 0 if * either object returned from the accessor chain "get" call is null. */ public int compare (Object obj1, Object obj2) { this.count++; if ((obj1 == null) || (obj2 == null) ) { return 0; } if ((!this.clazz.isAssignableFrom (obj1.getClass ())) || (!this.clazz.isAssignableFrom (obj2.getClass ())) ) { throw new IllegalArgumentException ("Expected objects of type: " + this.clazz.getName () + ", got: " + obj1.getClass ().getName () + ", and: " + obj2.getClass ().getName ()); } try { for (int i = 0; i < this.fields.size (); i++) { SortField f = (SortField) this.fields.get (i); Object val1 = f.getValue (obj1); Object val2 = f.getValue (obj2); // Can't compare what's not there, return 0. if ((val1 == null) || (val2 == null) ) { return 0; } int v = 0; if (val1 instanceof Comparable) { // We can use a simple compareTo. Comparable comp = (Comparable) val1; v = comp.compareTo (val2); } else { v = val1.toString ().compareTo (val2.toString ()); } if (v != 0) { if (f.getType ().equals (GeneralComparator.DESC)) { return -1 * v; } // This is an ascending sort... return v; } // They are equal, so need to go to the next field... continue; } } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } return 0; } /** * Implement the {@link Comparator.equals(Object)} method. * We just look through our fields and then compare the fields. * * @param obj Another GeneralComparator. * @return true if all our fields match those in the passed in * GeneralComparator AND that they are in the same order AND that they * have the same type, false otherwise. */ public boolean equals (Object obj) { GeneralComparator gc = (GeneralComparator) obj; List oFields = gc.getFields (); if (oFields.size () != this.fields.size ()) { return false; } for (int i = 0; i < this.fields.size (); i++) { SortField f = (SortField) this.fields.get (i); SortField of = (SortField) oFields.get (i); if (f.compareTo (of) != 0) { return false; } } return true; } /** * Return a List of GeneralComparator.SortField objects, this is used in * the {@link #equals(Object)} method. * * @return A List of GeneralComparator.SortField objects. */ protected List getFields () { return this.fields; } public int getCount () { return this.count; } /** * Get the comparator data as a JDOM element. * * @return The built element. */ /* public Element getAsJDOMElement () { Element root = new Element (GeneralComparator.XMLConstants.root); // Add the class. root.setAttribute (GeneralComparator.XMLConstants.clazz, this.clazz.getName ()); // Add all the fields. for (int i = 0; i < this.fields.size (); i++) { SortField sf = (SortField) this.fields.get (i); Element e = new Element (GeneralComparator.XMLConstants.field); e.setAttribute (GeneralComparator.XMLConstants.id, sf.getField ()); e.setAttribute (GeneralComparator.XMLConstants.type, sf.getType ()); root.addContent (e); } return root; } */ private class SortField implements Comparable { private String field = ""; private String type = GeneralComparator.ASC; private Getter get = null; public SortField (String field, String type, Class c) throws IllegalArgumentException { this (new Getter (field, c), type, c); this.field = field; } public SortField (Getter field, String type, Class c) throws IllegalArgumentException { // Get the field or method... this.get = field; //this.field = field; if ((!type.equals (GeneralComparator.ASC)) && (!type.equals (GeneralComparator.DESC)) ) { throw new IllegalArgumentException ("Type must be either: " + GeneralComparator.ASC + " or: " + GeneralComparator.DESC); } this.type = type; } public Object getValue (Object obj) throws IllegalAccessException, InvocationTargetException { return this.get.getValue (obj); } protected String getField () { return this.field; } protected String getType () { return this.type; } public int compareTo (Object o) { SortField f = (SortField) o; if ((this.field.equals (f.getField ())) && (this.type.equals (f.getType ())) ) { return 0; } if (this.field.equals (f.getField ())) { return this.type.compareTo (f.getType ()); } return this.field.compareTo (f.getField ()); } } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/GeneralFilter.java000066400000000000000000002121371157743670400257740ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Constructor; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.SortedMap; import java.util.Comparator; import java.util.Iterator; import java.util.Set; import java.util.SortedSet; import java.util.Collection; import java.util.Date; public class GeneralFilter { public static final int EQUALS = 0; public static final int NOT_EQUALS = 9; public static final int GREATER_THAN = 1; public static final int LESS_THAN = 2; public static final int CONTAINS = 3; public static final int NOT_CONTAINS = 10; private static final int IN_RANGE = 4; public static final int STARTS_WITH = 5; public static final int ENDS_WITH = 6; public static final int KEYS = 7; public static final int VALUES = 8; private List fields = new ArrayList (); private Class clazz = null; private boolean nullAcceptPolicy = false; public GeneralFilter (Class c) { this.clazz = c; } /** * Specify what the policy should be when a null value is returned from an * accesor chain, in other words, when the comparison is made should * a null value return true or false. * * @param policy The policy to use. */ public void setNullAcceptPolicy (boolean policy) { this.nullAcceptPolicy = policy; } /** * Get the null accept policy, this governs what happens when a null is * observed from the result of an accessor chain call, true * indicates that the value is accepted, false indicates * that it is rejected. * * @return The policy. */ public boolean getNullAcceptPolicy () { return this.nullAcceptPolicy; } /** * Add a new field to check for the date to be in the specified range. * * @param field The field spec. * @param max The upper date. * @param min The lower date. * @throws IllegalArgumentException If the field spec is not valid for the * class set in the constructor. */ public void addField (String field, Date max, Date min) throws IllegalArgumentException { this.fields.add (new DateFilterField (field, max, min, null, GeneralFilter.IN_RANGE, this.clazz)); } /** * Set new date range values for the specified date filter field. * * @param field The field spec. * @param max The upper date. * @param min The lower date. * @throws IllegalArgumentException If we can't find the specified field. */ public void setFieldValue (String field, Date max, Date min) throws IllegalArgumentException { // Get the field. FilterField ff = this.getField (field); if (ff == null) { throw new IllegalArgumentException ("Field: " + field + " not found."); } if (!(ff instanceof DateFilterField)) { throw new IllegalArgumentException ("Field: " + field + " filters on: " + ff.c.getName () + " but expected to filter on: " + Date.class.getName ()); } DateFilterField f = (DateFilterField) ff; f.max = max; f.min = min; } /** * Add a new field for a Date comparison. * Note: the only supported types for ths method are: * GeneralFilter.EQUALS, GeneralFilter.NOT_EQUALS, GeneralFilter.LESS_THAN, * GeneralFilter.GREATER_THAN. * * @param field The field spec. * @param value The Date to check against. * @param type The type, see above for valid values. * @throws IllegalArgumentException If the field spec is not valid for the * class set in the constructor. Or if you pass in an invalid * type for this method. */ public void addField (String field, Date value, int type) throws IllegalArgumentException { this.fields.add (new DateFilterField (field, null, null, value, type, this.clazz)); } /** * Set a new date value for the specified date filter field. * * @param field The field spec. * @param value The new value. * @throws IllegalArgumentException If we can't find the specified field. */ public void setFieldValue (String field, Date value) throws IllegalArgumentException { // Get the field. FilterField ff = this.getField (field); if (ff == null) { throw new IllegalArgumentException ("Field: " + field + " not found."); } if (!(ff instanceof DateFilterField)) { throw new IllegalArgumentException ("Field: " + field + " filters on: " + ff.c.getName () + " but expected to filter on: " + Date.class.getName ()); } DateFilterField f = (DateFilterField) ff; f.value = value; } /** * Add a new field for a boolean comparison. * Note: the only supported types for ths method are: * GeneralFilter.EQUALS, GeneralFilter.NOT_EQUALS. * * @param field The field spec. * @param value The Date to check against. * @param type The type, see above for valid values. * @throws IllegalArgumentException If the field spec is not valid for the * class set in the constructor. Or if you pass in an invalid * type for this method. */ public void addField (String field, boolean value, int type) throws IllegalArgumentException { this.fields.add (new BooleanFilterField (field, value, type, this.clazz)); } /** * Set a new value for the specified boolean filter field. * * @param field The field spec. * @param value The new value. * @throws IllegalArgumentException If we can't find the specified field. */ public void setFieldValue (String field, boolean value) throws IllegalArgumentException { // Get the field. FilterField ff = this.getField (field); if (ff == null) { throw new IllegalArgumentException ("Field: " + field + " not found."); } if (!(ff instanceof BooleanFilterField)) { throw new IllegalArgumentException ("Field: " + field + " filters on: " + ff.c.getName () + " but expected to filter on: " + Boolean.class.getName ()); } BooleanFilterField f = (BooleanFilterField) ff; f.value = value; } /** * Add a new field for a number comparison. Even though you should pass in * a double ANY number can be checked for, regardless of primitive * type. *

* Note: the only supported types for ths method are: * GeneralFilter.EQUALS, GeneralFilter.NOT_EQUALS, GeneralFilter.LESS_THAN, * GeneralFilter.GREATER_THAN. *

* Warning: it is valid to pass in a value of 4 for the * type, which maps to a range check for the values however this is NOT * recommended since in this method we set the maximum and minimum values * to 0, so unless the value you are looking for is 0 you would get * false returned from the checking of the field in the filter, * probably not what you want... If you want to check a range then * use {@link #addField(String,double,double)}. * * @param field The field spec. * @param value The value to check against. * @param type The type, see above for valid values. * @throws IllegalArgumentException If the field spec is not valid for the * class set in the constructor. Or if you pass in an invalid * type for this method. */ public void addField (String field, double value, int type) throws IllegalArgumentException { this.fields.add (new NumberFilterField (field, 0, 0, value, type, this.clazz)); } /** * Set new maximum and minimum values for the specified number filter field. * * @param field The field spec. * @param value The new value. * @throws IllegalArgumentException If we can't find the specified field. */ public void setFieldValue (String field, double value) throws IllegalArgumentException { // Get the field. FilterField ff = this.getField (field); if (ff == null) { throw new IllegalArgumentException ("Field: " + field + " not found."); } if (!(ff instanceof NumberFilterField)) { throw new IllegalArgumentException ("Field: " + field + " filters on: " + ff.c.getName () + " but expected to filter on: " + Number.class.getName ()); } NumberFilterField f = (NumberFilterField) ff; f.val = value; } /** * Add a new field for a number range comparison. Even though you should pass in * a double ANY number can be checked for, regardless of primitive * type. The checking is such that if the value equals one of the values (max or min) then * we find a match. * * @param field The field spec. * @param max The maximum value to check against. * @param min The minimum value to check against. * @throws IllegalArgumentException If the field spec is not valid for the * class set in the constructor. */ public void addField (String field, double max, double min) { this.fields.add (new NumberFilterField (field, max, min, 0, GeneralFilter.IN_RANGE, this.clazz)); } /** * Set new maximum and minimum values for the specified number filter field. * * @param field The field spec. * @param max The new maximum to check against. * @param min The new minimum to check against. * @throws IllegalArgumentException If we can't find the specified field. */ public void setFieldValue (String field, double max, double min) throws IllegalArgumentException { // Get the field. FilterField ff = this.getField (field); if (ff == null) { throw new IllegalArgumentException ("Field: " + field + " not found."); } if (!(ff instanceof NumberFilterField)) { throw new IllegalArgumentException ("Field: " + field + " filters on: " + ff.c.getName () + " but expected to filter on: " + Number.class.getName ()); } NumberFilterField f = (NumberFilterField) ff; f.max = max; f.min = min; } /** * Add a new field for a String comparison. *

* Note: the only supported types for ths method are: * GeneralFilter.EQUALS, GeneralFilter.NOT_EQUALS, GeneralFilter.CONTAINS, * GeneralFilter.NOT_CONTAINS, GeneralFilter.STARTS_WITH, GeneralFilter.ENDS_WITH. *

* Warning: using this method will cause the toString () method * to be called on the value we get from the field regardless of type, if you * need to compare objects then use the method {@link #addField(String,Object,Comparator,int)} * or {@link #addField(String,Object,int)}. * * @param field The field spec. * @param value The value to compare against. * @param type The type of comparison, see above for valid values. * @throws IllegalArgumentException If the field spec is not valid for the * class set in the constructor. Or if you pass in an invalid * type for this method. */ public void addField (String field, String value, int type) throws IllegalArgumentException { this.fields.add (new StringFilterField (field, value, type, this.clazz)); } /** * Set the value for the specified String filter field. * * @param field The field spec. * @param value The value to compare against. * @throws IllegalArgumentException If we can't find the specified field. */ public void setFieldValue (String field, String value) throws IllegalArgumentException { // Get the field. FilterField ff = this.getField (field); if (ff == null) { throw new IllegalArgumentException ("Field: " + field + " not found."); } if (!(ff instanceof StringFilterField)) { throw new IllegalArgumentException ("Field: " + field + " filters on: " + ff.c.getName () + " but expected to filter on: " + String.class.getName ()); } StringFilterField f = (StringFilterField) ff; f.val = value; } /** * Add a new field for an Object comparison. The passed in Object MUST * implement the Comparable interface otherwise an exception if thrown. We compare * the object gained from the field spec to the object passed in by calling * compareTo([Object returned from field spec call]) passing the object * returned to the compareTo method of the object passed in here. It is your responsibility * to ensure that the object returned from the field spec call will NOT cause * a ClassCastException to be thrown in the compareTo method. *

* Note: the only supported types for ths method are: * GeneralFilter.EQUALS, GeneralFilter.NOT_EQUALS, GeneralFilter.LESS_THAN, * GeneralFilter.GREATER_THAN. * If you pass in GeneralFilter.NOT_EQUALS then if the compareTo method returns something * other than 0 then we accept the object in the filter. * * @param field The field spec. * @param value The value to compare against. * @param type The type of comparison, see above for valid values. * @throws IllegalArgumentException If the field spec is not valid for the * class set in the constructor. Or if you pass in an invalid * type for this method, or if the passed in object does not implement * the Comparable interface. */ public void addField (String field, Object object, int type) throws IllegalArgumentException { if (object.getClass ().isAssignableFrom (Comparable.class)) { throw new IllegalArgumentException ("Object does implement the: " + Comparable.class.getName () + " interface."); } this.fields.add (new ObjectFilterField (field, object, null, type, this.clazz)); } /** * Set the value for the specified field. * * @param field The field spec. * @param value The value to compare against. * @throws IllegalArgumentException If we can't find the specified field. */ public void setFieldValue (String field, Object value) throws IllegalArgumentException { // Get the field. FilterField ff = this.getField (field); if (ff == null) { throw new IllegalArgumentException ("Field: " + field + " not found."); } if (!(ff instanceof ObjectFilterField)) { throw new IllegalArgumentException ("Field: " + field + " filters on: " + ff.c.getName () + " but expected to filter on: " + Object.class.getName ()); } ObjectFilterField f = (ObjectFilterField) ff; f.obj = value; } /** * Add a new field for an Object comparison. We use the passed in * Comparator to compare the objects, in the call to the compare * method we pass the object passed into this method as the first argument * and the object returned from the field spec call as the second argument. *

* It is your responsibility * to ensure that the object passed in and returned from the field spec call will NOT cause * a ClassCastException to be thrown in the compare method. *

* Note: the only supported types for ths method are: * GeneralFilter.EQUALS, GeneralFilter.NOT_EQUALS, GeneralFilter.LESS_THAN, * GeneralFilter.GREATER_THAN. * If you pass in GeneralFilter.NOT_EQUALS then if the compare method returns something * other than 0 then we accept the object in the filter. * * @param field The field spec. * @param value The value to compare against. * @param type The type of comparison, see above for valid values. * @throws IllegalArgumentException If the field spec is not valid for the * class set in the constructor. Or if you pass in an invalid * type for this method, or if the passed in object does not implement * the Comparable interface. */ public void addField (String field, Object object, Comparator comp, int type) throws IllegalArgumentException { this.fields.add (new ObjectFilterField (field, object, comp, type, this.clazz)); } /** * Get the named field, we get the field from our list of fields. * * @param field The field spec. * @return The FilterField (well the sub-class) or null if we can't * find the field. */ private FilterField getField (String field) { for (int i = 0; i < this.fields.size (); i++) { FilterField f = (FilterField) this.fields.get (i); if (f.field.equals (field)) { return f; } } return null; } /** * Get the class that we are filtering. * * @return The Class. */ public Class getFilterClass () { return this.clazz; } /** * Cycle over all our fields and check to see if this object * matches. We return at the first field that does not match. * * @param o The object to check the fields against. * @return true if all our fields match, false * otherwise. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public boolean accept (Object o) throws IllegalAccessException, InvocationTargetException, FilterException { // Cycle over our filters and do it... for (int i = 0; i < this.fields.size (); i++) { FilterField f = (FilterField) this.fields.get (i); if (!f.accept (o)) { return false; } } return true; } /** * Iterate over the Set and filter the objects it contains. * Any objects that match, via the {@link #accept(Object)} * method will then be added to the newSet parameter. Since we use * an Iterator to cycle over the Set the ordering of the values added * to the newSet is dependent on the ordering of the newSet Set. *

* The Set set is left unchanged. * * @param set The Set to filter. * @param newSet The Set to add successfully filtered objects to. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filter (Set set, Set newSet) throws IllegalAccessException, InvocationTargetException, FilterException { Iterator iter = set.iterator (); while (iter.hasNext ()) { Object o = iter.next (); if (this.accept (o)) { newSet.add (o); } } } /** * Iterate over the Set and filter the objects it contains. * Any objects that match, via the {@link #accept(Object)} * method will then be added to the newList parameter. Since we use * an Iterator to cycle over the Set the ordering of the values added * to the newList is dependent on the ordering of the set Set. *

* The Set set is left unchanged. * * @param set The Set to filter. * @param newList The List to add successfully filtered objects to. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filter (Set set, List newList) throws IllegalAccessException, InvocationTargetException, FilterException { Iterator iter = set.iterator (); while (iter.hasNext ()) { Object o = iter.next (); if (this.accept (o)) { newList.add (o); } } } /** * Iterate over the List and filter the objects it contains. * Any objects that match, via the {@link #accept(Object)} * method will then be added to the newSet parameter. *

* The List list is left unchanged. * * @param list The List to filter. * @param newSet The Set to add successfully filtered objects to. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filter (List list, Set newSet) throws IllegalAccessException, InvocationTargetException, FilterException { int size = list.size (); for (int i = 0; i < size; i++) { Object o = list.get (i); if (this.accept (o)) { newSet.add (o); } } } /** * Iterate over the Map and filter either the Keys or Values in the Map * given our fields. You specify whether you want the Keys or Values to * be filtered using the type parameter, pass either GeneralFilter.KEYS * or GeneralFilter.VALUES. Any values that match, via the {@link #accept(Object)} * method will then be added to the newMap parameter. Since we use * an Iterator to cycle over the Map the ordering of the values added * to the newMap is dependent on the ordering of the newMap Map. *

* The Map map is left unchanged. *

* Note: if the type parm is NOT GeneralFilter.KEYS * or GeneralFilter.VALUES then it's assumed you want to filter on the values. * * @param map The Map to filter. * @param type The type to filter on, either keys or values. * @param newMap The map to add successfully filtered keys/values to. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filter (Map map, int type, Map newMap) throws IllegalAccessException, InvocationTargetException, FilterException { if ((type != GeneralFilter.KEYS) && (type != GeneralFilter.VALUES) ) { type = GeneralFilter.VALUES; } Iterator iter = map.keySet ().iterator (); while (iter.hasNext ()) { Object key = iter.next (); Object value = map.get (key); if (type == GeneralFilter.KEYS) { if (this.accept (key)) { newMap.put (key, value); } } if (type == GeneralFilter.VALUES) { if (this.accept (value)) { newMap.put (key, value); } } } } /** * Iterate over the Collection and filter given our fields. *

* The Collection collection is left unchanged. *

* Note: since we use an Iterator to iterate over the Collection * it is SLOWER than if you have a List and use the {@link #filter(List,List)} * method since that uses List.size and it has been shown that using the get * method can be an order of magnitude faster than using the Iterator. This * method is really here to allow filtering of things like Sets. * It would be interesting to determine whether performing the following would * be more efficient (i.e. faster...) than using the Iterator: *
     *   // Get as an Object array.
     *   Object[] objs = collection.toArray ();
     *
     *   int size = objs.length;
     *   for (int i = 0; i < size; i++)
     *   {
     *
     *       if (this.accept (objs[i]))
     *       {
     *
     *          newCollection.add (objs[i]);
     *
     *       }
     *
     *   } 
     * 
*

* If you find this to be the case, please contact code-monkey@gentlyweb.com. *

* The bottom line is, if you have a List to filter then use the {@link #filter(List,List)} * method rather than this one. *

* * @param collection The Collection to filter. * @param newCollection The Collection to add successfully filtered objects to. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filter (Collection collection, Collection newCollection) throws IllegalAccessException, InvocationTargetException, FilterException { Iterator iter = collection.iterator (); while (iter.hasNext ()) { Object val = iter.next (); if (this.accept (val)) { newCollection.add (val); } } } /** * Cycle over the List and filter given our fields. The filtered * objects are added to the newList in the order they are gained * from list. *

* The List list is left unchanged. *

* This method will be much quicker than using the {@link #filter(Collection,Collection)} * method since it uses the get method of List rather than an Iterator. However if * you need to iterate over the List rather than use direct access then * cast as a Collection and call {@link #filter(Collection,Collection)} instead. * * @param list The List to filter. * @param newList The List to add successfully filtered objects to. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filter (List list, List newList) throws IllegalAccessException, InvocationTargetException, FilterException { int size = list.size (); for (int i = 0; i < size; i++) { Object o = list.get (i); if (this.accept (o)) { newList.add (o); } } } /** * Filter an array of objects and return a new array of the filtered * objects. *

* The object array is left unchanged. *

* It should be noted that we perform a bit of a cheat here, we use * an intermediate ArrayList to add the new objects into and then * call toArray to get the objects. This may have efficiency * considerations but we're pretty sure that the implementation of * ArrayList is gonna be as fast we could write! * * @param objects The objects to filter. * @return A new Object array of the filtered objects. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public Object[] filter (Object[] objects) throws IllegalAccessException, InvocationTargetException, FilterException { List l = new ArrayList (); int length = objects.length; for (int i = 0; i < length; i++) { if (this.accept (objects[i])) { l.add (objects[i]); } } return l.toArray (); } /** * Iterate over the Map and filter either the Keys or Values in the Map * given our fields. You specify whether you want the Keys or Values to * be filtered using the type parameter, pass either GeneralFilter.KEYS * or GeneralFilter.VALUES. Any values that match, via the {@link #accept(Object)} * method will then be added to the new Map. *

* The Map is left unchanged. *

* Note: if the type parm is NOT GeneralFilter.KEYS * or GeneralFilter.VALUES then it's assumed you want to filter on the values. *

* We try and create a new instance of the same type as the Map passed in. * So if the Map passed in is actually a HashMap then we create a new * HashMap and then add to that. If the passed in Map actually is a * SortedMap then we call {@link #filter(SortedMap,int)} instead. There is * a potential problem here in that we can only call the default no argument * constructor for the new Map, if you are using a HashMap and have tuned * it with a load factor and capacity then this method will ruin that and * we recommend that you use: {@link #filter(Map,int,Map)} instead. * * @param map The Map to filter. * @param type The type to filter on, either keys or values. * @return A new Map to add successfully filtered keys/values to. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. It is also thrown * if we cannot create the new instance of Map * it will then contain a nested exception with the "real" * reason for the failure. */ public Map filter (Map map, int type) throws IllegalAccessException, InvocationTargetException, FilterException { // Get the class of the passed in Map. // See if it's really a SortedMap. Class c = map.getClass (); if (c.isAssignableFrom (SortedMap.class)) { return this.filter ((SortedMap) map, type); } // Create a new instance of the Map... Map nMap = null; try { nMap = (Map) c.newInstance (); } catch (Exception e) { throw new FilterException ("Unable to create new instance of: " + map.getClass ().getName () + ", root cause: " + e.getMessage (), e); } this.filter (map, type, nMap); return nMap; } /** * Iterate over the SortedMap and filter either the Keys or Values in the SortedMap * given our fields. You specify whether you want the Keys or Values to * be filtered using the type parameter, pass either GeneralFilter.KEYS * or GeneralFilter.VALUES. Any values that match, via the {@link #accept(Object)} * method will then be added to the new SortedMap. *

* The Map is left unchanged. *

* Note: if the type parm is NOT GeneralFilter.KEYS * or GeneralFilter.VALUES then it's assume you want to filter on the values. *

* We try and create a new instance of the same type as the SortedMap passed in. * And then get the Comparator from the old SortedMap and use it in the * constructor of the new SortedMap. If your SortedMap doesn't use a * Comparator then it doesn't matter since if your SortedMap follows the * general contract for a SortedMap then it should ignore the Comparator * value if it is null. If the SortedMap passed in doesn't have * a constructor with a single Comparator argument then we try and create * a new version via Class.newInstance (), i.e. via a blank * constructor, if that isn't present or not accessible then we * throw an exception. * * @param map The SortedMap to filter. * @param type The type to filter on, either keys or values. * @return A new SortedMap with the successfully filtered keys/values added to it. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. It is also thrown * if we cannot create the new instance of SortedMap * it will then contain a nested exception with the "real" * reason for the failure. */ public SortedMap filter (SortedMap map, int type) throws IllegalAccessException, InvocationTargetException, FilterException { // Get the constructor that has a single Comparator argument. SortedMap nMap = null; try { Class[] types = {Comparator.class}; Constructor con = map.getClass ().getConstructor (types); // Invoke it... try { Object[] parms = {map.comparator ()}; nMap = (SortedMap) con.newInstance (parms); } catch (Exception e) { throw new FilterException ("Unable to create new instance of: " + map.getClass ().getName () + " using the constructor that takes a single: " + Comparator.class.getName () + " argument, root cause: " + e.getMessage (), e); } } catch (Exception e) { // Try a new instance... try { nMap = (SortedMap) map.getClass ().newInstance (); } catch (Exception ee) { throw new FilterException ("Unable to create a new instance of: " + map.getClass ().getName () + ", cannot find no argument constructor or constructor that takes java.util.Comparator as it's only argument, root cause: " + ee.getMessage (), ee); } } this.filter (map, type, nMap); return nMap; } /** * Iterate over the Collection and filter given our fields, return the filtered * objects in a new Collection. *

* The Collection passed in is left unchanged. *

* Effectively this method is just a wrapper for {@link #filter(Collection,Collection)}. *

* The bottom line is, if you have a List to filter then use the {@link #filter(List,List)} * method rather than this one. *

* We try and create a new instance of the same type as the Collection passed in. * So if the Collection passed in is actually a ArrayList then we create a new * ArrayList and then add to that. If the passed in Collection actually is a * SortedSet then we call {@link #filter(SortedSet,int)} instead, this is to * preserve any Comparator that may be used in sorting the Collection. *

* * @param collection The Collection to filter. * @return A new Collection with the successfully filtered objects added. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. It is also thrown * if we cannot create the new instance of Collection * it will then contain a nested exception with the "real" * reason for the failure. */ public Collection filter (Collection collection) throws IllegalAccessException, InvocationTargetException, FilterException { // Get the class of the passed in Collection. // See if it's really a SortedSet. Class c = collection.getClass (); if (c.isAssignableFrom (SortedSet.class)) { return this.filter ((SortedSet) collection); } // Create a new instance of the Collection... Collection nCol = null; try { nCol = (Collection) c.newInstance (); } catch (Exception e) { throw new FilterException ("Unable to create new instance of: " + collection.getClass ().getName () + ", root cause: " + e.getMessage (), e); } this.filter (collection, nCol); return nCol; } /** * Iterate over the Set and filter given our fields, return the filtered * objects in a new Set. *

* The Set passed in is left unchanged. *

* Effectively this method is just a wrapper for {@link #filter(Set,Set)}. *

* We try and create a new instance of the same type as the Set passed in. * So if the Set passed in is actually a HashSet then we create a new * HashSet and then add to that. If the passed in Set actually is a * SortedSet then we call {@link #filter(SortedSet,int)} instead, this is to * preserve any Comparator that may be used in sorting the Set. *

* * @param set The Set to filter. * @return A new Set with the successfully filtered objects added. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. It is also thrown * if we cannot create the new instance of Set * it will then contain a nested exception with the "real" * reason for the failure. */ public Set filter (Set set) throws IllegalAccessException, InvocationTargetException, FilterException { // Get the class of the passed in Collection. // See if it's really a SortedSet. Class c = set.getClass (); if (c.isAssignableFrom (SortedSet.class)) { return this.filter ((SortedSet) set); } // Create a new instance of the Set... Set nSet = null; try { nSet = (Set) c.newInstance (); } catch (Exception e) { throw new FilterException ("Unable to create new instance of: " + set.getClass ().getName () + ", root cause: " + e.getMessage (), e); } this.filter (set, nSet); return nSet; } /** * Iterate over the SortedSet and filter the objects it contains. * Any values that match, via the {@link #accept(Object)} * method will then be added to the new SortedMap. *

* The SortedSet is left unchanged. *

* We try and create a new instance of the same type as the SortedSet passed in. * And then get the Comparator from the old SortedMet and use it in the * constructor of the new SortedMet. If your SortedMet doesn't use a * Comparator then it doesn't matter since if your SortedMet follows the * general contract for a SortedMet then it should ignore the Comparator * value if it is null. If the SortedMet passed in doesn't have * a constructor with a single Comparator argument then we try and create * a new version via Class.newInstance (), i.e. via a blank * constructor, if that isn't present or not accessible then we * throw an exception. * * @param set The SortedMet to filter. * @return A new SortedMet with successfully filtered objects added to it. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. It is also thrown * if we cannot create the new instance of SortedSet * it will then contain a nested exception with the "real" * reason for the failure. */ public SortedSet filter (SortedSet set) throws IllegalAccessException, InvocationTargetException, FilterException { // Get the constructor that has a single Comparator argument. SortedSet nSet = null; try { Class[] types = {Comparator.class}; Constructor con = set.getClass ().getConstructor (types); // Invoke it... try { Object[] parms = {set.comparator ()}; nSet = (SortedSet) con.newInstance (parms); } catch (Exception e) { throw new FilterException ("Unable to create new instance of: " + set.getClass ().getName () + " using the constructor that takes a single: " + Comparator.class.getName () + " argument, root cause: " + e.getMessage (), e); } } catch (Exception e) { // Try a new instance... try { nSet = (SortedSet) set.getClass ().newInstance (); } catch (Exception ee) { throw new FilterException ("Unable to create a new instance of: " + set.getClass ().getName () + ", cannot find no argument constructor or constructor that takes java.util.Comparator as it's only argument, root cause: " + ee.getMessage (), ee); } } this.filter (set, nSet); return nSet; } /** * Cycle over the List and filter given our fields. The filtered * objects are added to a new List and then returned. *

* The List list is left unchanged. *

* This method will be much quicker than using the {@link #filter(Collection,Collection)} * method since it uses the get method of List rather than an Iterator. However if * you need to iterate over the List rather than use direct access then * cast as a Collection and call {@link #filter(Collection,Collection)} instead and then * cast the return as a List. *

* We try and create a new instance of the same type as the List passed in. * So if the List passed in is actually an ArrayList then we create a new * ArrayList and then add to that. *

* * @param list The List to filter. * @return A new List with the successfully filtered objects added to it. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. It is also thrown * if we cannot create the new instance of List * it will then contain a nested exception with the "real" * reason for the failure. */ public List filter (List list) throws IllegalAccessException, InvocationTargetException, FilterException { // Get the class of the passed in List. Class c = list.getClass (); // Create a new instance of the List. List nList = null; try { nList = (List) c.newInstance (); } catch (Exception e) { throw new FilterException ("Unable to create new instance of: " + list.getClass ().getName () + ", root cause: " + e.getMessage (), e); } this.filter (list, nList); return nList; } /** * Cycle over the List and filter given our fields directly from the passed in * List. The filtered objects are removed from the passed List. * This method will probably be slower than doing: *
     *   GeneralFilter gf = new GeneralFilter (MyObjectClass);
     *
     *   // ... configure the filter ...
     * 
     *   List myList = gf.filter (myList);
     * 
*

* This is because we have to here use an Iterator to strip out the unwanted * objects rather than using the get method which is what {@link #filter(List)} * uses. * * @param list The List to filter. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filterAndRemove (List list) throws IllegalAccessException, InvocationTargetException, FilterException { this.filterAndRemove ((Collection) list); } /** * Cycle over the Collection and filter given our fields directly from the passed in * Set. The filtered objects are removed from the passed Collection. * * @param col The Collection to filter. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filterAndRemove (Collection col) throws IllegalAccessException, InvocationTargetException, FilterException { Iterator iter = col.iterator (); while (iter.hasNext ()) { if (!this.accept (iter.next ())) { iter.remove (); } } } /** * Cycle over the Set and filter given our fields directly from the passed in * Set. The filtered objects are removed from the passed Set. * * @param set The Set to filter. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filterAndRemove (Set set) throws IllegalAccessException, InvocationTargetException, FilterException { this.filterAndRemove ((Collection) set); } /** * Iterate over the Map and filter given our fields directly from the passed in * Map. The filtered objects are removed from the passed Map. *

Note: if the type parm is NOT GeneralFilter.KEYS * or GeneralFilter.VALUES then it's assumed you want to filter on the values. * * @param set The Set to filter. * @throws InvocationTargetException If we cannot execute the associated * {@link Accessor} chain and get the * value. * @throws IllegalAccessException If we cannot execute the associated * {@link Accessor} chain because of a * security violation. * @throws FilterException Thrown if the return type is not what is * expected from the field type, for example if * we are checking a java.utli.Date but the returned type * is NOT of type java.util.Date. */ public void filterAndRemove (Map map, int type) throws IllegalAccessException, InvocationTargetException, FilterException { if ((type != GeneralFilter.KEYS) && (type != GeneralFilter.VALUES) ) { type = GeneralFilter.VALUES; } Iterator iter = map.keySet ().iterator (); while (iter.hasNext ()) { Object key = iter.next (); if (type == GeneralFilter.KEYS) { if (!this.accept (key)) { iter.remove (); } } if (type == GeneralFilter.VALUES) { if (!this.accept (map.get (key))) { iter.remove (); } } } } /** * Output the filter fields as a String suitable for debugging. */ public String toString () { StringBuffer buf = new StringBuffer (); buf.append ("Class: "); buf.append (this.clazz.getName ()); buf.append ('\n'); buf.append (" Fields (filter object class/type/field/value[/extras]:\n"); for (int i = 0; i < this.fields.size (); i++) { buf.append (" "); buf.append (this.fields.get (i).toString ()); buf.append ('\n'); } return buf.toString (); } private class ObjectFilterField extends FilterField { private Object obj = null; private Comparator comp = null; private int type = GeneralFilter.EQUALS; private ObjectFilterField (String field, Object obj, Comparator comp, int type, Class c) throws IllegalArgumentException { super (field, c); if ((type != GeneralFilter.EQUALS) && (type != GeneralFilter.NOT_EQUALS) && (type != GeneralFilter.LESS_THAN) && (type != GeneralFilter.GREATER_THAN) ) { throw new IllegalArgumentException (type + " is not supported for an Object comparison."); } this.obj = obj; this.comp = comp; this.type = type; } public String toString () { return Object.class.getName () + "/" + this.type + "/" + this.getField () + "/" + this.obj.toString () + "/" + this.comp.toString (); } protected boolean accept (Object o) throws IllegalAccessException, InvocationTargetException, FilterException { Object v = this.getValue (o); if (v == null) { return getNullAcceptPolicy (); } int res = 0; // See if we are using the comparator or the comparable interface... if (this.comp != null) { res = this.comp.compare (this.obj, v); } else { // Using the comparable interface... Comparable compObj = (Comparable) this.obj; // Do the compare... res = compObj.compareTo (o); } if (this.type == GeneralFilter.GREATER_THAN) { if (res > 0) { return true; } } if (this.type == GeneralFilter.LESS_THAN) { if (res < 0) { return true; } } if (this.type == GeneralFilter.EQUALS) { if (res == 0) { return true; } } if (this.type == GeneralFilter.NOT_EQUALS) { if (res != 0) { return true; } } return false; } } private class BooleanFilterField extends FilterField { private boolean value = false; private int type = GeneralFilter.EQUALS; private BooleanFilterField (String field, boolean value, int type, Class c) throws IllegalArgumentException { super (field, c); if ((type != GeneralFilter.EQUALS) && (type != GeneralFilter.NOT_EQUALS) ) { throw new IllegalArgumentException (type + " is not supported for a Boolean comparison."); } this.value = value; this.type = type; } public String toString () { return Boolean.class.getName () + "/" + this.type + "/" + this.getField () + "/" + this.value; } protected boolean accept (Object o) throws IllegalAccessException, InvocationTargetException, FilterException { Object v = this.getValue (o); if (v == null) { return getNullAcceptPolicy (); } // Get the type, if it is a java.lang.Boolean then grand... if (!v.getClass ().isAssignableFrom (Boolean.class)) { throw new FilterException ("Type of value returned from getter: " + this.getter.getType ().getName () + " is NOT of type: " + Boolean.class.getName ()); } boolean b = ((Boolean) v).booleanValue (); if (this.type == GeneralFilter.EQUALS) { if (b == this.value) { return true; } } if (this.type == GeneralFilter.NOT_EQUALS) { if (b != this.value) { return true; } } return false; } } private class DateFilterField extends FilterField { private Date max = null; private Date min = null; private Date value = null; private int type = GeneralFilter.EQUALS; private DateFilterField (String field, Date max, Date min, Date value, int type, Class c) throws IllegalArgumentException { super (field, c); if ((type != GeneralFilter.EQUALS) && (type != GeneralFilter.NOT_EQUALS) && (type != GeneralFilter.IN_RANGE) && (type != GeneralFilter.LESS_THAN) && (type != GeneralFilter.GREATER_THAN) ) { throw new IllegalArgumentException (type + " is not supported for a Date comparison."); } this.max = max; this.min = min; this.value = value; this.type = type; } public String toString () { return Date.class.getName () + "/" + this.type + "/" + this.getField () + "/" + this.value + "/" + "max:" + this.max + "/" + "min:" + this.min; } protected boolean accept (Object o) throws IllegalAccessException, InvocationTargetException, FilterException { Object v = this.getValue (o); if (v == null) { return getNullAcceptPolicy (); } // Get the type, if it is a java.util.Date then grand... if (!v.getClass ().isAssignableFrom (Date.class)) { throw new FilterException ("Type of value returned from getter: " + this.getter.getType ().getClass () + " is NOT of type: " + Date.class.getName ()); } Date d = (Date) v; if (this.type == GeneralFilter.EQUALS) { if (d.equals (this.value)) { return true; } } if (this.type == GeneralFilter.NOT_EQUALS) { if (!d.equals (this.value)) { return true; } } if (this.type == GeneralFilter.IN_RANGE) { if ((d.equals (this.max)) || (d.equals (this.min)) ) { return true; } if ((d.before (this.max)) && (d.after (this.min)) ) { return true; } } if (this.type == GeneralFilter.LESS_THAN) { if (d.before (this.value)) { return true; } } if (this.type == GeneralFilter.GREATER_THAN) { if (d.after (this.value)) { return true; } } return false; } } private class NumberFilterField extends FilterField { private double max = 0; private double min = 0; private double val = 0; private int type = GeneralFilter.EQUALS; private NumberFilterField (String field, double max, double min, double value, int type, Class c) throws IllegalArgumentException { super (field, c); if ((type != GeneralFilter.EQUALS) && (type != GeneralFilter.NOT_EQUALS) && (type != GeneralFilter.IN_RANGE) && (type != GeneralFilter.LESS_THAN) && (type != GeneralFilter.GREATER_THAN) ) { throw new IllegalArgumentException (type + " is not supported for a Number comparison."); } this.max = max; this.min = min; this.val = value; this.type = type; } public String toString () { return Number.class.getName () + "/" + this.type + "/" + this.getField () + "/" + this.val + "/" + "max:" + this.max + "/" + "min:" + this.min; } protected boolean accept (Object o) throws IllegalAccessException, InvocationTargetException, FilterException { Object v = this.getValue (o); if (v == null) { return getNullAcceptPolicy (); } // Get the type, if it is a java.lang.Number then grand... if (!v.getClass ().isAssignableFrom (Number.class)) { throw new FilterException ("Type of value returned from getter: " + this.getter.getType ().getName () + " is NOT of type: " + Number.class.getName ()); } // It is a number... // Good now get it as a double. double oVal = ((Number) v).doubleValue (); if (this.type == GeneralFilter.EQUALS) { if (oVal == this.val) { return true; } } if (this.type == GeneralFilter.NOT_EQUALS) { if (oVal != this.val) { return true; } } if (this.type == GeneralFilter.IN_RANGE) { if ((oVal <= this.max) && (oVal >= this.min) ) { return true; } } if (this.type == GeneralFilter.LESS_THAN) { if (oVal < this.val) { return true; } } if (this.type == GeneralFilter.GREATER_THAN) { if (oVal > this.val) { return true; } } return false; } } private class StringFilterField extends FilterField { private String val = ""; private int type = GeneralFilter.EQUALS; private StringFilterField (String field, String value, int type, Class c) throws IllegalArgumentException { super (field, c); if ((type != GeneralFilter.EQUALS) && (type != GeneralFilter.NOT_EQUALS) && (type != GeneralFilter.CONTAINS) && (type != GeneralFilter.NOT_CONTAINS) && (type != GeneralFilter.STARTS_WITH) && (type != GeneralFilter.ENDS_WITH) ) { throw new IllegalArgumentException (type + " is not supported for a String comparison."); } this.type = type; this.val = value; } public String toString () { return String.class.getName () + "/" + this.type + "/" + this.getField () + "/" + this.val; } protected boolean accept (Object o) throws IllegalAccessException, InvocationTargetException, FilterException { Object ro = this.getValue (o); if (ro == null) { return getNullAcceptPolicy (); } String v = ro.toString (); if (this.type == GeneralFilter.EQUALS) { if (v.equals (this.val)) { return true; } } if (this.type == GeneralFilter.NOT_EQUALS) { if (!v.equals (this.val)) { return true; } } if (this.type == GeneralFilter.NOT_CONTAINS) { if (v.indexOf (this.val) == -1) { return true; } } if (this.type == GeneralFilter.CONTAINS) { if (v.indexOf (this.val) != -1) { return true; } } if (this.type == GeneralFilter.ENDS_WITH) { if (v.endsWith (this.val)) { return true; } } if (this.type == GeneralFilter.STARTS_WITH) { if (v.startsWith (this.val)) { return true; } } return false; } } private abstract class FilterField { protected Getter getter = null; private Class c = null; private String field = null; private FilterField (String field, Class c) throws IllegalArgumentException { this.field = field; this.c = c; this.getter = new Getter (field, c); } protected Object getValue (Object o) throws IllegalAccessException, InvocationTargetException { return this.getter.getValue (o); } protected String getField () { return this.field; } protected abstract boolean accept (Object o) throws IllegalAccessException, InvocationTargetException, FilterException; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/GeneralUtils.java000066400000000000000000000143561157743670400256520ustar00rootroot00000000000000package com.gentlyweb.utils; import java.util.Map; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Arrays; import java.util.StringTokenizer; import java.lang.reflect.Method; import java.io.StringWriter; import java.io.PrintWriter; import java.io.IOException; /** * A Class to hold some general purpose Utilities that don't have a home * anywhere else (at the moment). *

* You should NOT rely on these methods staying in this class. *

* All methods in this class are static. */ public class GeneralUtils { public static String GENTLYWEB_EMAIL = "code-monkey@gentlyweb.com"; /** * This method is here for historical reasons and is just a wrapper around * {@link StringUtils.replaceString(java.lang.String,java.lang.String,java.lang.String)} * and will be removed soon! It should NOT be used! * * @param text The text to perform the replacement on. * @param str The string to find in the text. * @param replace The string to replace "str" with. * @return A new String representing the replaced text (if any * were made). * @throws NullPointerException If any of the arguments are null indicating which one is at fault. */ /* public static String replaceString (String text, String str, String replace) throws NullPointerException { return StringUtils.replaceString (text, str, replace); } */ /** * Get an exception (and if it's an instance of {@link ChainException}) the chain of * exceptions (if present) as a String. * * @param e The exception * @return A String of the exception chain. * @throws IOException Should never happen since we are using a StringWriter. */ public static String getExceptionTraceAsString (Exception e) throws IOException { StringWriter sout = new StringWriter (); PrintWriter pout = new PrintWriter (sout); e.printStackTrace (pout); pout.println (); if (e instanceof ChainException) { ChainException ee = (ChainException) e; ee.printInnerExceptionChain (pout); } return sout.toString (); } /** * * Validate an IPv4 address (string) passed in, it must conform to the following rules: *

* * @param ipaddress The ip address to validate. * @throws Exception If one of the rules is broken. */ public static void validateIPv4Address (String ipaddress) throws Exception { // Rules here are: // // Must have 4 parts. // Parts must be separated by . // The first octet must be in the range 0-223. // The second and third octets must be in the range 0-255 // 4th octet can be either a number in the range 1-254 (0 is the name of the network, 255 is the // broadcast address...) // All parts must be numbers... StringTokenizer t = new StringTokenizer (ipaddress, "."); if (t.countTokens () != 4) { throw new Exception ("IP address: " + ipaddress + ", does not consist of 4 parts"); } String octet1 = t.nextToken (); String octet2 = t.nextToken (); String octet3 = t.nextToken (); String lastpart = t.nextToken (); try { int intoctet1 = Integer.parseInt (octet1); if ((intoctet1 > 223) || (intoctet1 < 0) ) { throw new Exception ("First octet of IP address: " + ipaddress + " must be in the range 0-223"); } } catch (NumberFormatException nfe) { throw new Exception ("First octet of IP address: " + ipaddress + ", is not a number"); } try { int intoctet2 = Integer.parseInt (octet2); if ((intoctet2 > 255) || (intoctet2 < 0) ) { throw new Exception ("Second octet of IP address: " + ipaddress + " must be in the range 0-255"); } } catch (NumberFormatException nfe) { throw new Exception ("Second octet of IP address: " + ipaddress + ", is not a number"); } try { int intoctet3 = Integer.parseInt (octet3); if ((intoctet3 > 255) || (intoctet3 < 0) ) { throw new Exception ("Third octet of IP address: " + ipaddress + " must be in the range 0-255"); } } catch (NumberFormatException nfe) { throw new Exception ("Third octet of IP address: " + ipaddress + ", is not a number"); } // Now handle the fourth octet. // This is just a number...or supposed to be... try { int intlastpart = Integer.parseInt (lastpart); if ((intlastpart > 254) || (intlastpart < 1) ) { throw new Exception ("Final octet of IP address: " + ipaddress + " must be in the range 1-254"); } } catch (NumberFormatException nfe) { throw new Exception ("Final octet of IP address: " + ipaddress + ", is not a number"); } } public static void getMethods (Class c, String name, int mods, List ms) { if (c == null) { return; } Method[] meths = c.getDeclaredMethods (); for (int i = 0; i < meths.length; i++) { Method m = meths[i]; if ((m.getName ().equals (name)) && ((m.getModifiers () & mods) == mods) ) { if (!ms.contains (m)) { // This is one. ms.add (m); } } } // Now get all the super-classes. Class sup = c.getSuperclass (); if (sup != null) { GeneralUtils.getMethods (sup, name, mods, ms); } // Now work up through the super-classes/interfaces. Class[] ints = c.getInterfaces (); for (int i = 0; i < ints.length; i++) { Class in = ints[i]; GeneralUtils.getMethods (in, name, mods, ms); } } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/Getter.java000066400000000000000000000216261157743670400245040ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.ArrayList; import java.util.StringTokenizer; /** * This class is used to perform access into a Java object using a * String value with a specific notation. *

* The Accessor uses a dot notation such as field1.method1.method2 to * perform the access on an object. Each value in the notation refers to * a field or method (a no argument method) of the type of the previous * value. * For instance if you have the following class structure: *

*
 * public class A 
 * {
 *    public B = new B ();
 * }
 * 
 * public class B
 * {
 *    public C = new C ();
 * }
 * 
 * public class C
 * {
 *    String d = "";
 * }
 * 
*

* You would then use the notation: B.C.d to get access to * field d in Class C. *

* The Accessor also supports a [ ] notation for accessing * into Lists/Maps and Arrays. If the value between the [ ] * is an integer then we look for the associated type to be either * an array or a List, we then index into it with the integer. If * the value is NOT an integer then we use assume the * type is a Map and use it as a key into the Map. *

* For instance changing the example above: *

*
 * public class A 
 * {
 *    public List vals = new ArrayList ();
 * }
 * 
*

* Now we could use: vals[X] where X is a positive integer. * Or changing again: *

*
 * public class A 
 * {
 *    public Map vals = new HashMap ();
 * }
 * 
*

* We could use: vals[VALUE] where VALUE would then be * used as a Key into the vals HashMap. *

* Note: The Accessor is NOT designed to be an all purpose * method of gaining access to a class. It has specific uses and for * most will be of no use at all. It should be used for general purpose * applications where you want to access specific fields of an object * without having to know the exact type. One such application is in * the {@link GeneralComparator}, in that case arbitrary Objects can * be sorted without having to write complex Comparators or implementing * the Comparable interface AND it gives the flexibility that sorting * can be changed ad-hoc. *

* The Accessor looks for in the following order: *

*

* Note: we have had to add the 3rd type to allow for methods that don't follow * JavaBeans conventions (there are loads in the standard Java APIs which makes * accessing impossible otherwise). */ public class Getter { private List chain = new ArrayList (); private Class clazz = null; private int cs = 0; private String acc = null; /** * Get the getter associated with the named reference. Return * null if there isn't one, or if we can't access it. * * @param ref The reference for the getter. * @param clazz The Class to get the field from. */ public Getter (String ref, Class clazz) throws IllegalArgumentException { if (clazz == null) { throw new IllegalArgumentException ("Class must be specified"); } this.acc = ref; this.clazz = clazz; StringTokenizer t = new StringTokenizer (ref, "."); Class c = clazz; while (t.hasMoreTokens ()) { String tok = t.nextToken (); String index = ""; // Get the Fields. Field[] fields = c.getFields (); Field f = null; // See if the token matches... for (int i = 0; i < fields.length; i++) { if (fields[i].getName ().equals (tok)) { // Found it... f = fields[i]; break; } } if (f != null) { c = f.getType (); this.chain.add (f); } else { Method m = this.getNoParmJavaGetMethod (tok, c); if (m == null) { throw new IllegalArgumentException ("Cannot find method with name: " + tok + " in class: " + c.getName ()); } // Need to set the method as being accessible here to workaround // an annoying Java reflection bug that seems to have been around // since the year dot. See bug: 4071957. m.setAccessible (true); c = m.getReturnType (); if (Void.class.isAssignableFrom (c)) { throw new IllegalArgumentException ("Method: " + m.getName () + " cannot be called on class: " + c.getName () + " since return type is void"); } this.chain.add (m); } } this.cs = this.chain.size (); } public Class getBaseClass () { return this.clazz; } /** * Get the class of the type of object we would return from the {@link #getValue(Object)} * method. * * @return The class. */ public Class getType () { Object o = this.chain.get (this.chain.size () - 1); // See what type the accessor is... if (o instanceof Method) { Method m = (Method) o; return m.getReturnType (); } if (o instanceof Field) { // It's a field...so... Field f = (Field) o; return f.getType (); } return null; } public Object getValue (Object obj) throws IllegalAccessException, InvocationTargetException { // If the object is null then return null. if (obj == null) { return null; } // For our accessor chain, use the Field and Methods // to get the actual value. Object retdata = obj; for (int i = 0; i < this.cs; i++) { Object o = this.chain.get (i); // See what type the accessor is... if (o instanceof Method) { Method m = (Method) o; Object[] parms = {}; // Invoke the method... try { retdata = m.invoke (retdata, parms); } catch (Exception e) { this.throwException (obj, e); } if (retdata == null) { return null; } } if (o instanceof Field) { // It's a field...so... Field f = (Field) o; // Now get the value... try { retdata = f.get (retdata); } catch (Exception e) { this.throwException (obj, e); } } } return retdata; } private void throwException (Object o, Exception e) { throw new RuntimeException ("Unable to get value from instance of: " + o.getClass ().getName () + ", using accessor: " + this.acc + " expected type to be: " + this.clazz.getName (), e); } public static Method getNoParmJavaGetMethod (String method, Class c) { StringBuffer b = new StringBuffer (method); Method m = null; // First look for a "get" method. try { b.setCharAt (0, Character.toUpperCase (method.charAt (0))); b.insert (0, "get"); String getMN = b.toString (); m = c.getMethod (getMN, null); if (m != null) { return m; } } catch (Exception e) { // Painful to have to do it this way... } try { b = new StringBuffer (method); b.setCharAt (0, Character.toUpperCase (method.charAt (0))); b.insert (0, "is"); String isMN = b.toString (); m = c.getMethod (isMN, null); if (m != null) { return m; } } catch (Exception e) { // Sigh... } try { return c.getMethod (method, null); } catch (Exception e) { // Ignore... } return null; } public String getAccessor () { return this.acc; } public String toString () { return "Accessor: " + this.acc + " from class: " + this.clazz.getName (); } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/Grouper.java000066400000000000000000000056631157743670400247000ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.Collections; public class Grouper { private List getters = new ArrayList (); private Class c = null; public Grouper (Class c) { this.c = c; } public void addGroupBy (Getter get) throws IllegalArgumentException { // Get the class that the getter relates to, they MUST be the same class AND the // same object... classes loaded via other classloaders are not compatible. if (get.getBaseClass ().hashCode () != this.c.hashCode ()) { throw new IllegalArgumentException ("Class in Getter is: " + get.getBaseClass ().getName () + " with hashCode: " + get.getBaseClass ().hashCode () + " which is incompatible with comparator class: " + this.c.getName () + " with hashCode: " + this.c.hashCode ()); } this.getters.add (get); } public void addGroupBy (String on) { this.getters.add (new Getter (on, this.c)); } public Map group (List items) throws ChainException { Map groups = new HashMap (); for (int j = 0; j < items.size (); j++) { Object o = items.get (j); boolean match = true; List key = new ArrayList (); for (int i = 0; i < this.getters.size (); i++) { // Ensure that all getters match (equals = true) // Get the value. Object val = null; try { val = ((Getter) this.getters.get (i)).getValue (o); } catch (Exception e) { throw new ChainException ("Unable to get value for accessor from class: " + o.getClass ().getName (), e); } key.add (val); } List g = (List) groups.get (key); if (g == null) { g = new ArrayList (); groups.put (key, g); } g.add (o); } return groups; } public List groupSortByGroupSize (List items, String ascDesc) throws ChainException { Map m = this.group (items); List nSearches = new ArrayList (); CollectionsUtils.addMapEntriesToList (m, nSearches); GeneralComparator gc = new GeneralComparator (List.class); gc.addField ("size", ascDesc); Collections.sort (nSearches, gc); return nSearches; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/HTMLDataSource.java000066400000000000000000000031771157743670400257720ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import javax.activation.DataSource; public class HTMLDataSource implements DataSource { private String html; public HTMLDataSource (String html) { this.html = html; } public InputStream getInputStream () throws IOException { if (this.html == null) { throw new IOException ("No HTML provided"); } return new BufferedInputStream (new ByteArrayInputStream (this.html.getBytes())); } public OutputStream getOutputStream () throws IOException { throw new IOException ("Output not supported"); } public String getContentType() { return "text/html; charset=\"iso-8859-1\""; } public String getName() { return "HTML DataSource for sending only"; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/IOUtils.java000066400000000000000000000271761157743670400246100ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.io.InputStream; import java.io.OutputStream; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.PrintWriter; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.util.zip.GZIPOutputStream; public class IOUtils { /** * Copy a file from one location to a new location. * * @param oldFile The old file name. * @param newFile The new file location. * @param bufSize The buffer size to use when performing the copy. * @throws IOException If we can't perform the copy. */ public static void copyFile (File oldFile, File newFile, int bufSize) throws IOException { BufferedInputStream bin = null; BufferedOutputStream bout = null; try { bin = new BufferedInputStream (new FileInputStream (oldFile)); bout = new BufferedOutputStream (new FileOutputStream (newFile)); IOUtils.streamTo (bin, bout, bufSize); /* byte buf[] = new byte[bufSize]; int bRead = -1; while ((bRead = bin.read (buf)) != -1) { bout.write (buf, 0, bRead); } */ } finally { if (bin != null) { bin.close (); } if (bout != null) { bout.flush (); bout.close (); } } } /** * GZIP a file, this will move the file from the * given name to the new location. This leaves the * old file in place! * * @param file The existing file name of the file. * @param newFile The new file location for the file. * @throws ChainException If we can't perform the transfer, the inner exception will * contain an IOException that is the "real" exception. */ public static void gzipFile (File file, File newFile) throws ChainException { // GZIP the file... try { BufferedInputStream oldFileRead = new BufferedInputStream (new FileInputStream (file)); // Get a GZIP output stream to the new CV file... GZIPOutputStream gzout = new GZIPOutputStream (new BufferedOutputStream (new FileOutputStream (newFile))); try { // Read the bytes from the old and write to the new. byte[] buf = new byte[4096]; IOUtils.streamTo (oldFileRead, gzout, 4096); /* int bytesRead = 0; while ((bytesRead = oldFileRead.read (buf, 0, buf.length)) != -1) { gzout.write (buf, 0, bytesRead); } */ } catch (IOException e) { throw new ChainException ("Unable to gzip file: " + file.getPath () + " to: " + newFile.getPath (), e); } finally { if (oldFileRead != null) { oldFileRead.close (); } if (gzout != null) { gzout.flush (); gzout.close (); } } } catch (IOException e) { throw new ChainException ("Unable to gzip file: " + file.getPath () + " to: " + newFile.getPath (), e); } } /** * Get a file length as kilobytes in the form x.y. * * @param length The length of the file. * @return A String formatted as x.y. */ public static String getFileLengthAsFormattedKilobytes (long length) { long kilos = length / 1024; long tenths = (length - (kilos * 1024)) / 100; return kilos + "." + tenths; } /** * Get a file length as formatted string. *

* * @param length The length of the file. * @return A String formatted as indicated above. */ public static String getFormattedFileLength (long length) { long meg = 1024 * 1024; String val = "B"; String num = ""; if (length >= meg) { val = "MB"; } if ((length >= 1024) && (length <= meg) ) { val = "KB"; } if (length >= meg) { length = length / 1024; } if (length >= 1024) { long m = length / 1024; long n = (length - (m * 1024)) / 100; while (String.valueOf (n).length () > 2) { n = n / 100; } return m + "." + n + " " + val; } else { return length + " " + val; } } /** * This method acts as a "join" between an input and an output stream, all it does is * take the input and keep reading it in and sending it directly to the output using the * specified buffer size. In this way you can join any input and output streams to pass * the data between them. Note: this method does NOT flush or close either stream (this is * to allow the "channel" to remain open and re-used. * * @param in The input stream. * @param out The output stream. * @param bufSize The buffer size. * @throws IOException If an IO exception occurs. */ public static void streamTo (InputStream in, OutputStream out, int bufSize) throws IOException { byte buf[] = new byte[bufSize]; int bRead = -1; while ((bRead = in.read (buf)) != -1) { out.write (buf, 0, bRead); } } /** * Write the given bytes to a file, note that this method will just overwrite * any existing file. * * @param file The file to write to. * @param bytes The byte array. * @param bufSize The size of output buffer to use, set to -1 to have the buffer size set to * bytes.length. * @throws IOException If the array cannot be written to the file. */ public static void writeBytesToFile (File file, byte[] bytes, int bufSize) throws IOException { if (bufSize <= 0) { bufSize = bytes.length; } BufferedInputStream bin = null; BufferedOutputStream bout = null; try { bin = new BufferedInputStream (new ByteArrayInputStream (bytes)); bout = new BufferedOutputStream (new FileOutputStream (file)); IOUtils.streamTo (bin, bout, bufSize); } finally { if (bin != null) { bin.close (); } if (bout != null) { bout.flush (); bout.close (); } } } /** * Write the given bytes to a file, note that this method will just overwrite * any existing file. For more control over the output buffer size use: {@link IOUtils#writeBytesToFile(File,byte[],int)}. * * @param file The file to write to. * @param bytes The byte array. * @throws IOException If the array cannot be written to the file. */ public static void writeBytesToFile (File file, byte[] bytes) throws IOException { IOUtils.writeBytesToFile (file, bytes, bytes.length); } /** * Write the given String to the File. * * @param file The file to write to. * @param str The value to write. * @throws IOException This should never happen because PrintWriter will * catch it. */ public static void writeStringToFile (File file, String str, boolean compress) throws IOException { OutputStream out = new BufferedOutputStream (new FileOutputStream (file)); if (compress) { out = new GZIPOutputStream (out); } PrintWriter pout = new PrintWriter (out); pout.print (str); pout.flush (); pout.close (); } /** * Get the contents of a File as a String. Remember that this method is * constrained by the underlying limits of an array size, since that can only * be as big as Integer.MAX_VALUE any file bigger than that cannot be returned. * Although why you would want to read a file into memory bigger than 2GB is anyone's * guess. * * @param file The file to read in. * @return The content of the file as a String. * @throws IOException If there is a problem with the read. * @throws IllegalArgumentException If the file length is greater than 232-1 since * the maximum size of an array is (max)int - 1. */ public static String getFile (File file) throws IOException { return new String (IOUtils.getFileAsArray (file)); } /** * Get the contents of a File as a byte array. We use a BufferedInputStream * and read the entire file in one go. * * @param file The file to read in. * @return The content of the file as a byte array. * @throws IOException If there is a problem with the read. * @throws IllegalArgumentException If the file length is greater than 232-1 since * the maximum size of an array is (max)int - 1. */ public static byte[] getFileAsArray (File file) throws IOException { if (file == null) { throw new IOException ("No file specified!"); } if (!file.exists ()) { throw new IOException ("File: " + file + " does not exist."); } long length = file.length (); if (length > Integer.MAX_VALUE) { throw new IllegalArgumentException ("File: " + file.getPath () + " is greater than maximum value: " + Integer.MAX_VALUE); } BufferedInputStream bin = null; byte[] chars = new byte[(int) length]; try { bin = new BufferedInputStream (new FileInputStream (file)); bin.read (chars, 0, (int) length); } finally { if (bin != null) { bin.close (); } } return chars; } /** * Ask a question of the User on stdout and wait for a response. *

* Take a positive as either "", "y|Y" or "yes|YES" or any case value for * yes and return true. Everything else is a no and * returns false. * * @param question The question to ask. * @return true if they responded with y|yes etc...return * false otherwise. * @throws IOException If there is an io problem. */ public static boolean getYNFromUser (String question) throws IOException { System.out.print (question); System.out.println (" [y|n, default y]"); BufferedReader bread = new BufferedReader (new InputStreamReader (System.in)); // Read a line... String line = bread.readLine (); // Check what the line is... line = line.trim (); line = line.toLowerCase (); boolean val = false; if ((line.equals ("")) || (line.equals ("y")) || (line.equals ("yes")) ) { val = true; } if (!val) { System.out.println ("Taking answer as n"); } System.out.println (); return val; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/ItemPair.java000066400000000000000000000035311157743670400247570ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.Map.Entry; public class ItemPair implements Entry { private Object key = null; private Object value = null; public ItemPair (Object key, Object value) { this.key = key; this.value = value; } /** * This method just uses the algorithm defined in the Map.Entry interface. * * @return The hash code. */ public int hashCode () { int k = 0; int v = 0; if (this.key != null) { k = this.value.hashCode (); } if (this.value != null) { v = this.value.hashCode (); } return k ^ v; } public Object getKey () { return this.key; } public Object setValue (Object value) { Object old = this.value; this.value = value; return old; } public Object getValue () { return this.value; } /** * This method just uses the algorithm defined in the Map.Entry interface. * * @return The hash code. */ public boolean equals (Object o) { if (!(o instanceof ItemPair)) { return false; } ItemPair ipo = (ItemPair) o; if ((ipo.getKey ().equals (this.key)) && (ipo.getValue ().equals (this.value)) ) { return true; } return false; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/MultipleObjectCacheManager.java000066400000000000000000000075221157743670400304120ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.Map; /** * This interface should be implemented by classes that contain X number of publicly available * ObjectCaches, it provides methods that allow applications to control the caches without * understanding what is inside the cache. *

* All of the methods in this interface are optional and if not supported then * implementing classes should throw UnsupportedOperationException. *

* This interface mirrors {@link ObjectCacheManager} but each method (where appropriate) * takes a key object to tell the implementing class which cache it wants the operation * to occur on. */ public interface MultipleObjectCacheManager { /** * Let the object cache be flushed. * * @param key The key to identify the particular cache. */ public void flush (Object key); /** * Set the maximum size of the cache. * * @param key The key to identify the particular cache. * @param size The maximum size. */ public void setMaxSize (Object key, int size); /** * Resize the cache to a particular size, if the size is actually bigger than the * current size then this operation should not touch the cached objects, if the size is * less then the cache should be reduced in size using the current policy until the * size is reached. Either way the maximum size should be set to this value. * * @param key The key to identify the particular cache. * @param size The new size. */ public void resize (Object key, int size); /** * Return the current capacity of the cache, it should basically be (max size - current size). * * @param key The key to identify the particular cache. * @return The current number of items that can be added until the cache reaches it's maximum size. */ public int capacity (Object key); /** * Return whether the cache is empty or not. * * @param key The key to identify the particular cache. * @return true if the cache is empty, false if it has entries. */ public boolean isEmpty (Object key); /** * Get all the entries in the cache as a Map of key to value. * * @param key The key to identify the particular cache. * @param map The Map that should be populated with the key/values in the cache. */ public void toMap (Object object, Map map); /** * Merge the current cache with another. * * @param key The key to identify the particular cache. * @param cache The cache to merge. */ public void merge (Object key, ObjectCache cache); /** * Add all the entries in the Map to cache. * * @param key The key to identify the particular cache. * @param map The Map to get key/values from. */ public void putAll (Object key, Map map); /** * Set the policy for managing the cache, should be one of: * {@link ObjectCache.OLDEST}, {@link ObjectCache.YOUNGEST}, {@link ObjectCache.RANDOM}. * * @param key The key to identify the particular cache. * @param policy The policy. */ public void setPolicy (Object key, int policy); } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/NumberUtils.java000066400000000000000000000056331157743670400255230ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; public class NumberUtils { /** * Get the passed in double to the required precision. * In essence if you have a number: 11.77987 and you would like it * to 2 digits decimal digit precision then call this method with digits * set to 2, in which case: 11.78 will be returned. This method * is useful when you want to round a number after a multiple * decimal place division. *

* In essence it merely rounds the decimal part to the required number of * digits using Math.round. *

* This method is most useful when provided in a suitable wrapper, for instance: * * public float getAsCurrency (float v) * { * * return (float) GeneralUtils.toPrecision (v, 2); * * } * *

* Note, it is safe to pass 0 as either of the parameters, it should be noted * that passing 0 as digits has the same effect as calling: Math.round (v), * which makes sense since 0 digit precision of 11.77987 should be 12. * * @param v The value to round. * @param digits The number of decimal digits to round to. * @return The rounded value. */ public static double toPrecision (double v, int digits) { if (v == 0) { return v; } double mult = Math.pow (10, digits); double r = Math.floor (v); return r + ((Math.round ((v - r) * mult)) / mult); } /** * Given an int value treat it as a number of days and return * the number of milliseconds for that number of days. * * @param days Number of days. * @return A long giving the number of milliseconds. */ public static long getDaysAsMillis (int days) { return (long) days * 86400000; } /** * Convert a number of milliseconds into seconds, we format to 2 decimal * places, i.e. we return a String of the form a.xy. * * @param millis The milliseconds to format. * @return A String formatted as a.xy. */ public static String getMillisAsFormattedSeconds (long millis) { long secs = millis / 1000; long tenths = (millis - (secs * 1000)) / 100; long hundredths = (millis - (secs * 1000) - (tenths * 100)) / 10; return secs + "." + tenths + hundredths; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/ObjectCache.java000066400000000000000000000364131157743670400254040ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.List; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import java.util.HashMap; import java.util.Random; import java.lang.reflect.InvocationTargetException; /** * The ObjectCache is it's own manager which means that classes that extend this one can * do so quickly and without having to implement the {@link ObjectCacheManager} interface * they only have to provide their own functionality whilst client classes can rely on the * interface. All operations defined with {@link ObjectCacheManager} are supported here. */ public class ObjectCache implements ObjectCacheManager { public static final int OLDEST = 1; public static final int YOUNGEST = 3; public static final int RANDOM = 5; private TreeMap keys = new TreeMap (); private Map data = new HashMap (); private int type = ObjectCache.RANDOM; private int maxSize = -1; private Random random = null; public ObjectCache (int policy) throws IllegalArgumentException { if ((type != ObjectCache.OLDEST) && (type != ObjectCache.YOUNGEST) && (type != ObjectCache.RANDOM) ) { throw new IllegalArgumentException ("Incorrect policy: " + type); } if (type == ObjectCache.RANDOM) { this.random = new Random (); } this.type = type; } public void setPolicy (int type) throws IllegalArgumentException { if ((type != ObjectCache.OLDEST) && (type != ObjectCache.YOUNGEST) && (type != ObjectCache.RANDOM) ) { throw new IllegalArgumentException ("Incorrect policy: " + type); } this.type = type; } public int getPolicy () { return this.type; } public Iterator iterator () { return new ObjectCacheIterator (this); } /** * Return a List of all the keys in the cache. * * @return The List (ArrayList) of all the keys in the cache. */ public synchronized List keys () { List l = new ArrayList (this.keys.size ()); l.addAll (this.keys.keySet ()); return l; } /** * Return a List of all the values in the cache. * * @return The List (ArrayList) of all the values in the cache. */ public synchronized List values () { List l = new ArrayList (this.data.size ()); Iterator iter = this.data.keySet ().iterator (); while (iter.hasNext ()) { l.add (this.data.get (iter.next ())); } return l; } /** * Return a List of keys in the cache that match the conditions imposed * by the {@link GeneralFilter} AND are applied to the values NOT the keys. * * @param f The filter to use. * @return A List of the keys that map to the matched values. * @throws IllegalAccessException Thrown by the {@link GeneralFilter.accept(Object)} * method if we can't get access to a value defined in the * GeneralFilter for the value class. * @throws InvocationTargetException Thrown by the {@link GeneralFilter.accept(Object)} * method if (as a result of accessing a field) an exception * is thrown by a value object. * @throws FilterException Thrown by the {@link GeneralFilter.accept(Object)} * method if (as a result of accessing a field) the type of * object returned by the field access is not of the expected type. */ public synchronized List keysForFilteredValues (GeneralFilter f) throws IllegalAccessException, InvocationTargetException, FilterException { List ks = this.keys (); List retData = new ArrayList (); Class c = f.getFilterClass (); for (int i = 0; i < ks.size (); i++) { Object k = ks.get (i); Object v = this.get (k); if (c.isAssignableFrom (v.getClass ())) { if (f.accept (v)) { retData.add (k); } } } return retData; } /** * Return a List of all the values in the cache that match * the conditions imposed by the {@link GeneralFilter} passed in. * We first gain all the values in the cache and then pass them * through the filter returning the values that match. Because a GeneralFilter * can only filter on a single class type (but the values may not be of a single type) * we ignore any values that are not of the type specified for the GeneralFilter. * * @return A List (ArrayList) of all the values in the cache. * @throws IllegalAccessException Thrown by the {@link GeneralFilter.accept(Object)} * method if we can't get access to a value defined in the * GeneralFilter for the value class. * @throws InvocationTargetException Thrown by the {@link GeneralFilter.accept(Object)} * method if (as a result of accessing a field) an exception * is thrown by a value object. * @throws FilterException Thrown by the {@link GeneralFilter.accept(Object)} * method if (as a result of accessing a field) the type of * object returned by the field access is not of the expected type. */ public synchronized List values (GeneralFilter f) throws IllegalAccessException, InvocationTargetException, FilterException { List vs = this.values (); List retData = new ArrayList (); Class c = f.getFilterClass (); for (int i = 0; i < vs.size (); i++) { Object v = vs.get (i); if (c.isAssignableFrom (v.getClass ())) { if (f.accept (v)) { retData.add (v); } } } return retData; } /** * Return a List of all the keys in the cache that match * the conditions imposed by the {@link GeneralFilter} passed in. * We first gain all the keys in the cache and then pass them * through the filter returning the keys that match. Because a GeneralFilter * can only filter on a single class type (but the keys may not be of a single type) * we ignore any keys that are not of the type specified for the GeneralFilter. * * @return A List (ArrayList) of all the keys in the cache. * @throws IllegalAccessException Thrown by the {@link GeneralFilter.accept(Object)} * method if we can't get access to a value defined in the * GeneralFilter for the key class. * @throws InvocationTargetException Thrown by the {@link GeneralFilter.accept(Object)} * method if (as a result of accessing a field) an exception * is thrown by a key object. * @throws FilterException Thrown by the {@link GeneralFilter.accept(Object)} * method if (as a result of accessing a field) the type of * object returned by the field access is not of the expected type. */ public synchronized List keys (GeneralFilter f) throws IllegalAccessException, InvocationTargetException, FilterException { List ks = this.keys (); List retData = new ArrayList (); Class c = f.getFilterClass (); for (int i = 0; i < ks.size (); i++) { Object k = ks.get (i); if (c.isAssignableFrom (k.getClass ())) { if (f.accept (k)) { retData.add (k); } } } return retData; } public synchronized void valuesToList (List list) { Iterator iter = this.keys.keySet ().iterator (); while (iter.hasNext ()) { Object key = iter.next (); Key d = (Key) this.keys.get (key); Object value = this.data.get (d); list.add (value); } } public synchronized void keysToList (List list) { Iterator iter = this.keys.keySet ().iterator (); while (iter.hasNext ()) { Object key = iter.next (); list.add (key); } } public synchronized void toMap (Map map) { Iterator iter = this.keys.keySet ().iterator (); while (iter.hasNext ()) { Object key = iter.next (); Key d = (Key) this.keys.get (key); Object value = this.data.get (d); map.put (key, value); } } public synchronized void putAll (Map map) { Iterator iter = map.keySet ().iterator (); while (iter.hasNext ()) { Object key = iter.next (); Object value = map.get (key); Key d = new Key (); d.key = key; this.data.put (d, value); this.keys.put (key, d); } } public boolean containsKey (Object key) { return this.keys.containsKey (key); } public synchronized void merge (ObjectCache cache) { Iterator iter = cache.iterator (); while (iter.hasNext ()) { Object key = iter.next (); Date date = cache.getLastAccessTime (key); Key d = new Key (); d.key = key; d.date.setTime (date.getTime ()); Object value = cache.get (key); this.data.put (d, value); this.keys.put (key, d); } } private boolean between (Date d, Date from, Date to) { boolean yes = false; if ((d.equals (from)) || (d.after (from)) ) { yes = true; } if (yes) { yes = false; if ((d.equals (to)) || (d.before (to)) ) { yes = true; } } return yes; } Iterator keysIterator () { return this.keys.keySet ().iterator (); } public synchronized ObjectCache cacheSliceTo (Date to) { return this.cacheSlice (new Date (0), to); } public synchronized ObjectCache cacheSliceFrom (Date from) { return this.cacheSlice (from, new Date (Long.MAX_VALUE)); } public synchronized ObjectCache cacheSlice (Date from, Date to) { ObjectCache c = new ObjectCache (this.getPolicy ()); c.setMaxSize (this.getMaxSize ()); Iterator iter = this.keys.keySet ().iterator (); while (iter.hasNext ()) { Object key = iter.next (); Key d = (Key) this.keys.get (key); if (this.between (d.date, from, to)) { Object value = this.data.get (d); c.put (key, value); c.setLastAccessTime (key, d.date); } } return c; } synchronized void setLastAccessTime (Object key, Date date) { if (this.keys.containsKey (key)) { Key d = (Key) this.keys.get (key); d.date.setTime (date.getTime ()); } } public synchronized Map sliceFrom (Date from) { return this.slice (from, new Date (Long.MAX_VALUE)); } public synchronized Map sliceTo (Date to) { return this.slice (new Date (0), to); } public synchronized Map slice (Date from, Date to) { Map map = new HashMap (); Iterator iter = this.keys.keySet ().iterator (); while (iter.hasNext ()) { Object key = iter.next (); Key d = (Key) this.keys.get (key); if (this.between (d.date, from, to)) { Object value = this.data.get (d); map.put (key, value); } } return map; } public Date getLastAccessTime (Object key) { return ((Key) this.keys.get (key)).date; } public boolean isEmpty () { return this.keys.size () == 0; } public int capacity () { if (this.maxSize == -1) { return this.maxSize; } return this.maxSize - this.keys.size (); } public int getMaxSize () { return this.maxSize; } public void setMaxSize (int size) { if (size < 1) { return; } this.maxSize = size; } public Object get (Object key) { Key d = (Key) this.keys.get (key); if (d == null) { return null; } d.touch (); return this.data.get (d); } public Object firstValue () { Key d = (Key) this.keys.get (this.keys.firstKey ()); d.touch (); return this.data.get (d); } public Object lastValue () { Key d = (Key) this.keys.get (this.keys.lastKey ()); d.touch (); return this.data.get (d); } public Object firstKey () { return this.keys.firstKey (); } public int size () { return this.keys.size (); } public synchronized void remove (Object key) { Key d = null; if (this.keys.containsKey (key)) { d = (Key) this.keys.get (key); this.data.remove (d); this.keys.remove (key); } } public void resize (int size) { if (size > 0) { this.maxSize = size; this.resize (); } } protected synchronized void resize () { if (this.maxSize > 0) { while (this.keys.size () > this.maxSize) { Object key = null; if (this.type == ObjectCache.OLDEST) { key = this.keys.lastKey (); } if (this.type == ObjectCache.YOUNGEST) { key = this.keys.firstKey (); } if (this.type == ObjectCache.RANDOM) { if (this.random == null) { this.random = new Random (); } int val = this.random.nextInt (this.keys.size ()); Object[] _keys = this.keys.keySet ().toArray (); key = _keys[val]; } Key d = (Key) this.keys.get (key); this.data.remove (d); this.keys.remove (key); } } } public synchronized void put (Object key, Object value) { Key d = null; // Check to see if we will go over our max size. this.resize (); if (this.keys.containsKey (key)) { d = (Key) this.keys.get (key); d.touch (); } else { d = new Key (); d.key = key; this.keys.put (key, d); } this.data.put (d, value); } /** * Clear our data structures. */ public synchronized void flush () { this.keys.clear (); this.data.clear (); } private class Key implements Comparable { private Object key = null; private Date date = new Date (); public int compareTo (Object o) { Key k = (Key) o; return this.date.compareTo (k.date); } public void touch () { this.date.setTime (System.currentTimeMillis ()); } public boolean equals (Object o) { Key k = (Key) o; return this.key.equals (k.key) && this.date.equals (k.date); } public int hasCode () { return key.hashCode () + date.hashCode (); } } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/ObjectCacheIterator.java000066400000000000000000000021411157743670400271050ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.Iterator; public class ObjectCacheIterator implements Iterator { private Iterator iter = null; public ObjectCacheIterator (ObjectCache cache) { this.iter = cache.keysIterator (); } public void remove () { throw new UnsupportedOperationException ("Remove not supported for ObjectCaches."); } public Object next () { return this.iter.next (); } public boolean hasNext () { return this.iter.hasNext (); } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/ObjectCacheManager.java000066400000000000000000000045211157743670400266720ustar00rootroot00000000000000package com.gentlyweb.utils; import java.util.Map; /** * This interface should be implemented by classes that contain a publicly available * ObjectCache, it provides methods that allow applications to control the cache without * understanding what is inside the cache. *

* All of the methods in this interface are optional and if not supported then * implementing classes should throw UnsupportedOperationException. */ public interface ObjectCacheManager { /** * Let the object cache be flushed. */ public void flush (); /** * Set the maximum size of the cache. * * @param size The maximum size. */ public void setMaxSize (int size); /** * Resize the cache to a particular size, if the size is actually bigger than the * current size then this operation should not touch the cached objects, if the size is * less then the cache should be reduced in size using the current policy until the * size is reached. Either way the maximum size should be set to this value. * * @param size The new size. */ public void resize (int size); /** * Return the current capacity of the cache, it should basically be (max size - current size). * * @return The current number of items that can be added until the cache reaches it's maximum size. */ public int capacity (); /** * Return whether the cache is empty or not. * * @return true if the cache is empty, false if it has entries. */ public boolean isEmpty (); /** * Get all the entries in the cache as a Map of key to value. * * @param map The Map that should be populated with the key/values in the cache. */ public void toMap (Map map); /** * Merge the current cache with another. * * @param cache The cache to merge. */ public void merge (ObjectCache cache); /** * Add all the entries in the Map to cache. * * @param map The Map to get key/values from. */ public void putAll (Map map); /** * Set the policy for managing the cache, should be one of: * {@link ObjectCache.OLDEST}, {@link ObjectCache.YOUNGEST}, {@link ObjectCache.RANDOM}. * * @param policy The policy. */ public void setPolicy (int policy); } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/StringRangeComparator.java000066400000000000000000000013331157743670400275160ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; public interface StringRangeComparator { public boolean inRange (Object o1, Object o2); } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/StringUtils.java000066400000000000000000000314561157743670400255430ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.Iterator; import java.text.DecimalFormat; public class StringUtils { /** * Convert a string to a set of character entities. * * @param str The string to convert. * @return The converted string. */ public static String convertStringToCharEntities (String str) { DecimalFormat df = new DecimalFormat ("000"); StringBuffer buf = new StringBuffer (); char chars[] = str.toCharArray (); for (int i = 0; i < chars.length; i++) { buf.append ("&#"); buf.append (df.format ((int) chars[i])); buf.append (';'); } return buf.toString (); } /** * Given a chunk of text format it so that you return a new String * with the line length equal to that passed in. * * @param text The text to format. * @param lineLength The line length to format the text to. * @return The formatted text. */ public static String getTextAsFormattedLines (String text, int lineLength) { StringBuffer retdata = new StringBuffer (); List lines = new ArrayList (); String t = new String (text); while (t.length () > lineLength) { // Chop up the description into lines close to lineLength... // Chop off the first lineLength characters... String s = t.substring (0, lineLength); String rest = t.substring (lineLength); // Find the last instance of " ". int lastInd = s.lastIndexOf (' '); // See if the last index if (lineLength - 1), if so then // we have a clean break... if (lastInd == (lineLength)) { retdata.append (s); retdata.append ('\n'); } else { // The last index is less so now // we need to check the rest to see // if that starts with a char other // than " ", if so it's part of the // previous word... if (rest.charAt (0) != ' ') { // It is! // Now get the rest of the word and append it // to s... int indexOfFirst = rest.indexOf (' '); String halfWord = rest.substring (0, indexOfFirst); // Substring... s = s + halfWord; // Add s to the list of lines. retdata.append (s); retdata.append ('\n'); rest = rest.substring (indexOfFirst + 1); } rest = rest.trim (); } t = rest; } lines.add (t.trim ()); retdata.append (t.trim ()); retdata.append ('\n'); return retdata.toString (); } /** * Indicate whether the String value has at least one * character in the range specified. * * @param value The value to check. * @param start The starting character of the range. * @param end The end character of the range. * @return Return true if it has at least * one character in the range, false * otherwise. */ public static boolean hasCharInRange (String value, char start, char end) { if (value == null) { return false; } char[] chars = value.toCharArray (); boolean found = false; for (int i = 0; i < chars.length; i++) { if ((chars[i] >= start) && (chars[i] <= end) ) { found = true; } } return found; } /** * A method to chop up a String on another multi-character String and * then return the results in a List. If you have a single character * String then you should use java.util.StringTokenizer. * * @param str The String to chop up. * @param token The token to look for. This should be just a plain * String, this method doesn't support regular expressions. * @return A List of all the Strings found, if token is not present then * return a single sized List with the str at 0. */ public static List tokenizeString (String str, String token) { List retData = new ArrayList (); String s = new String (str); int start = s.indexOf (token); if (start > -1) { while (start > -1) { String tok = s.substring (0, start); int end = start + token.length (); s = s.substring (end); if (!tok.equals ("")) { retData.add (tok); } start = s.indexOf (token); } if (!s.equals ("")) { retData.add (s); } } else { retData.add (s); } return retData; } /** * Determine whether the specified string contains a value, where * having a value means: * * (string != null) && (!string.equals ("")) * * * @return true if the string has a value, false * otherwise. */ public static boolean hasValue (String v) { if (v == null) { return false; } if (v.equals ("")) { return false; } return true; } /** * Given a Map of String/String (or Object/Object, they will be converted to Strings before * replacement) replace all instances of each one in the specified text. *

* There is no guarantee about the order in which the replacements will occur, if the * ordering is important then you should impose the order by using a sorted map. *

* The original text will not be affected. * * @param text The text to perform the replacements on. * @param map The map of String to find in the text and replace with the value of the map (String). * @param caseSensitive Indicate whether we should consider case when searching for the strings. * @return A new String representing the text with all the replacements made. If any of the keys/values in * the map are null then we return "". */ public static String replaceAllStrings (String text, Map map, boolean caseSensitive) { String newText = new String (text); Iterator iter = map.keySet ().iterator (); while (iter.hasNext ()) { Object k = iter.next (); String t = k.toString (); String v = (String) map.get (k); newText = StringUtils.replaceString (newText, t, v, caseSensitive); } return newText; } /** * Given a Map of String/String (or Object/Object, they will be converted to Strings before * replacement) replace all instances of each one in the specified text. *

* There is no guarantee about the order in which the replacements will occur, if the * ordering is important then you should impose the order by using a sorted map. *

* The original text will not be affected. * * @param text The text to perform the replacements on. * @param map The map of String to find in the text and replace with the value of the map (String). * @return A new String representing the text with all the replacements made. If any of the keys/values in * the map are null then we return "". */ public static String replaceAllStrings (String text, Map map) { String newText = text; Iterator iter = map.keySet ().iterator (); while (iter.hasNext ()) { Object k = iter.next (); String t = (String) k; String v = (String) map.get (k); newText = StringUtils.replaceString (newText, t, v); } return newText; } /** * Replace all instances of the specified string in the text given. * This will not recursively replace, if it finds something to replace * it replaces and then goes from the place it found last. *

* It should be noted that we take copies of text and str prior * to performing the replacements. *

* * @param text The text to perform the replacement on. * @param str The string to find in the text. * @param replace The string to replace "str" with. * @param caseSensitive Indicate whether we should consider case * when searching for the string. * @return A new String representing the replaced text (if any * were made). * @throws NullPointerExceptio If any of the arguments are null indicating which one is at fault. */ public static String replaceString (String text, String str, String replace, boolean caseSensitive) throws NullPointerException { if (text == null) { throw new NullPointerException ("text parm."); } if (str == null) { throw new NullPointerException ("str parm."); } if (replace == null) { throw new NullPointerException ("replace parm."); } // First convert text to a StringBuffer. StringBuffer buf = new StringBuffer (text); String newText = new String (text); String s = new String (str); if (!caseSensitive) { s = s.toLowerCase (); newText = newText.toLowerCase (); } int index = newText.indexOf (s); while (index != -1) { buf.replace (index, index + s.length (), replace); newText = buf.toString (); if (!caseSensitive) { newText = newText.toLowerCase (); } index = newText.indexOf (s, index + replace.length ()); } return buf.toString (); } /** * Replace all instances of the specified string in the text given. * This will not recursively replace, if it finds something to replace * it replaces and then goes from the place it found last. * * @param text The text to perform the replacement on. * @param str The string to find in the text. * @param replace The string to replace "str" with. * @return A new String representing the replaced text (if any * were made). * @throws NullPointerException If any of the arguments are null indicating which one is at fault. */ public static String replaceString (String text, String str, String replace) throws NullPointerException { if (text == null) { throw new NullPointerException ("text parm (arg 1)."); } if (str == null) { throw new NullPointerException ("str parm (arg 2)."); } if (replace == null) { throw new NullPointerException ("replace parm (arg3)."); } // First convert text to a StringBuffer. StringBuffer buf = new StringBuffer (text); int index = buf.indexOf (str); while (index != -1) { buf.replace (index, index + str.length (), replace); index = buf.indexOf (str, index + replace.length ()); } return buf.toString (); } /** * Indicate whether the String value has ALL of it's characters * in the specified range of characters. Make sure that * end is "greater" than start. If value is null return false. *

* Note: if you set start == end then you check to see if * a String is made up of the same character, not useful all the time but * it is sometimes. *

* * @param value The value to check. * @param start The starting character of the range. * @param end The end character of the range. * @return Whether all the characters are in the range. * Return true if they are, false * otherwise. */ public static boolean areAllCharsInRange (String value, char start, char end) { if (start > end) { return false; } if (value == null) { return false; } char[] chars = value.toCharArray (); for (int i = 0; i < chars.length; i++) { if ((chars[i] < start) || (chars[i] > end) ) { return false; } } return true; } /** * Given a particular String, remove any instances of the given * character from it and return a new String. * * @param str The String to search for the removals. * @param c The character to remove. * @return A String of the new String. */ public static String removeChar (String str, char c) { if (str == null) { return str; } StringBuffer buf = new StringBuffer (); char[] chars = str.toCharArray (); for (int i = 0; i < chars.length; i++) { if (chars[i] != c) { buf.append (chars[i]); } } return buf.toString (); } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/TimeDuration.java000066400000000000000000000143121157743670400256500ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.Date; public class TimeDuration { public static final String DEFAULT_FORMAT_SPEC = "DDDd HHh mmm sss SSSms"; private static final String DDD = "DDD"; private static final String SSS = "SSS"; private static final String ss = "ss"; private static final String HH = "HH"; private static final String mm = "mm"; private static final int milli = 1; private static final int sec = TimeDuration.milli * 1000; private static final int min = TimeDuration.sec * 60; private static final int hour = TimeDuration.min * 60; private static final int day = TimeDuration.hour * 24; private int days = 0; private int hours = 0; private int mins = 0; private int secs = 0; private int millis = 0; public TimeDuration (int days, int hours, int mins, int secs) { this.days = days; this.hours = hours; this.mins = mins; this.secs = secs; this.sanitize (); } public TimeDuration (Date from, Date to) { long f = 0; long t = 0; if (from != null) { f = from.getTime (); } if (to != null) { t = to.getTime (); } this.init (t - f); } public TimeDuration (Date d) { this.init (d.getTime ()); } public void init (long millis) { this.days = (int) (millis / TimeDuration.day); long rem = millis - (this.days * TimeDuration.day); this.hours = (int) (rem / TimeDuration.hour); rem = rem - (this.hours * TimeDuration.hour); this.mins = (int) (rem / TimeDuration.min); rem = rem - (this.mins * TimeDuration.min); this.secs = (int) (rem / TimeDuration.sec); this.millis = (int) (rem - (this.secs * TimeDuration.sec)); this.sanitize (); } public TimeDuration (long millis) { this.init (millis); } public TimeDuration (TimeDuration t) { this.init (t); } public TimeDuration (int days, int hours, int mins, int secs, int millis) { this.days = days; this.hours = hours; this.mins = mins; this.secs = secs; this.millis = millis; this.sanitize (); } private void sanitize () { if (this.days < 0) { this.days = 0; } if (this.hours < 0) { this.hours = 0; } if (this.hours > 23) { this.hours = 23; } if (this.mins < 0) { this.mins = 0; } if (this.mins > 59) { this.mins = 59; } if (this.secs < 0) { this.secs = 0; } if (this.secs > 59) { this.secs = 59; } if (this.millis < 0) { this.millis = 0; } if (this.millis > 999) { this.millis = 999; } } public void init (TimeDuration t) { this.days = t.getDays (); this.hours = t.getHours (); this.mins = t.getMins (); this.secs = t.getSecs (); this.millis = t.getMillis (); this.sanitize (); } public void subtract (TimeDuration t) { // Bit of a cheat this but it's the easiest way! TimeDuration tt = new TimeDuration (t.rollUpToMillis () - this.rollUpToMillis ()); this.init (tt); } public void add (TimeDuration t) { // Bit of a cheat this but it's the easiest way! TimeDuration tt = new TimeDuration (t.rollUpToMillis () + this.rollUpToMillis ()); this.init (tt); } public static TimeDuration getInstance (TimeDuration t) { return new TimeDuration (t); } public static TimeDuration getInstance (Timing t) { return new TimeDuration (t.getDuration ()); } public static TimeDuration getInstance (Date d) { return TimeDuration.getInstance (d.getTime ()); } public static TimeDuration getInstance (long millis) { return new TimeDuration (millis); } public Date getAsDate () { return new Date (this.rollUpToMillis ()); } public long rollUpToMillis () { return ((long) this.days * (long) TimeDuration.day) + (this.hours * TimeDuration.hour) + (this.mins * TimeDuration.min) + (this.secs * TimeDuration.sec) + (this.millis * TimeDuration.milli); } public void setMillis (int m) { this.millis = m; this.sanitize (); } public int getMillis () { return this.millis; } public void setSecs (int s) { this.secs = s; this.sanitize (); } public int getSecs () { return this.secs; } public void setMins (int m) { this.mins = m; this.sanitize (); } public int getMins () { return this.mins; } public void setHours (int h) { this.hours = h; this.sanitize (); } public int getHours () { return this.hours; } public void setDays (int d) { this.days = d; } public int getDays () { return this.days; } public String format () { return this.format (TimeDuration.DEFAULT_FORMAT_SPEC); } public String format (String spec) { String s = spec; if (spec.indexOf (TimeDuration.DDD) != -1) { s = StringUtils.replaceString (s, TimeDuration.DDD, String.valueOf (this.days)); } if (spec.indexOf (TimeDuration.SSS) != -1) { s = StringUtils.replaceString (s, TimeDuration.SSS, String.valueOf (this.millis)); } if (spec.indexOf (TimeDuration.ss) != -1) { s = StringUtils.replaceString (s, TimeDuration.ss, String.valueOf (this.secs)); } if (spec.indexOf (TimeDuration.HH) != -1) { s = StringUtils.replaceString (s, TimeDuration.HH, String.valueOf (this.hours)); } if (spec.indexOf (TimeDuration.mm) != -1) { s = StringUtils.replaceString (s, TimeDuration.mm, String.valueOf (this.mins)); } return s; } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/Timing.java000066400000000000000000000026461157743670400245020ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.Date; public class Timing { private long start = 0; private long end = 0; public Timing () { this.start = System.currentTimeMillis (); } public Timing (Date s) { this.start = s.getTime (); } public Timing (long s) { this.start = s; } public TimeDuration getDuration () { return new TimeDuration (this.end - this.start); } public void restart (Date d) { this.start = d.getTime (); this.end = 0; } public void restart () { this.start = System.currentTimeMillis (); this.end = 0; } public void stop (long s) { this.end = s; } public void stop (Date d) { this.end = d.getTime (); } public void stop () { this.end = System.currentTimeMillis (); } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/Timings.java000066400000000000000000000057271157743670400246700ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.util.Date; import java.util.Map; import java.util.HashMap; import java.util.TreeMap; import java.util.LinkedHashMap; import java.util.Iterator; public class Timings { public static final int TIME_ORDERED = 0; public static final int RANDOM = 1; public static final int KEY_ORDERED = 2; private Map ts = null; public Timings (int type) { if (type == Timings.TIME_ORDERED) { this.ts = new LinkedHashMap (); return; } if (type == Timings.KEY_ORDERED) { this.ts = new TreeMap (); return; } this.ts = new HashMap (); } public Iterator iterator () { if (this.ts instanceof TreeMap) { Map m = new TreeMap (this.ts); return m.keySet ().iterator (); } if (this.ts instanceof LinkedHashMap) { Map m = new LinkedHashMap (this.ts); return m.keySet ().iterator (); } Map m = new HashMap (this.ts); return m.keySet ().iterator (); } public void stop (Object k, Date d) { Timing t = (Timing) this.ts.get (k); if (t != null) { t.stop (d); } } public void stop (Object k, long d) { Timing t = (Timing) this.ts.get (k); if (t != null) { t.stop (d); } } public void stop (Object k) { Timing t = (Timing) this.ts.get (k); if (t != null) { t.stop (); } } public void start (Object k) { Timing t = (Timing) this.ts.get (k); if (t == null) { t = new Timing (); this.ts.put (k, t); } } public void start (Object k, long s) { Timing t = (Timing) this.ts.get (k); if (t == null) { t = new Timing (s); this.ts.put (k, t); } } public Timing remove (Object k) { return (Timing) this.ts.remove (k); } public Timing get (Object k) { return (Timing) this.ts.get (k); } public TimeDuration getDuration (Object k) { Timing t = this.get (k); if (t == null) { return null; } return t.getDuration (); } public long getDurationAsLong (Object k) { Timing t = this.get (k); if (t == null) { return 0; } return t.getDuration ().rollUpToMillis (); } public void start (Object k, Date s) { Timing t = (Timing) this.ts.get (k); if (t == null) { t = new Timing (s); this.ts.put (k, t); } } } gentlyweb-utils-1.5/src/utils/com/gentlyweb/utils/URLDataSource.java000066400000000000000000000034541157743670400256660ustar00rootroot00000000000000/* * Copyright 2006 - Gary Bentley * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gentlyweb.utils; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.BufferedInputStream; import javax.activation.DataSource; import java.net.URL; import java.net.URLConnection; public class URLDataSource implements DataSource { private URL url = null; private String contentType = "text/plain"; public URLDataSource (URL url) { this.url = url; } public InputStream getInputStream () throws IOException { if (this.url == null) { throw new IOException ("No URL provided"); } URLConnection urlC = url.openConnection (); urlC.setDoInput (true); urlC.connect (); this.contentType = urlC.getContentType (); // Get the input stream... return new BufferedInputStream (urlC.getInputStream ()); } public OutputStream getOutputStream () throws IOException { throw new IOException ("Output not supported"); } public String getContentType() { return this.contentType; } public String getName() { return "URL DataSource for sending only"; } }