All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.viaoa.object.OAFinder Maven / Gradle / Ivy

The newest version!
/*  Copyright 1999 Vince Via [email protected]
    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.viaoa.object;

import java.lang.reflect.*;
import java.util.*;

import com.viaoa.filter.*;
import com.viaoa.hub.*;
import com.viaoa.util.*;

// 20140124
/**
 * This is used to find all values from one OAObject/Hub to another OAObject/Hub, using a propertyPath. Support is included for Filters.
 *
 * @param  type of hub or OAObject to use as the root (from)
 * @param  type of hub for the to class (to).
 *        

* examples: // from Router, find all UserLogin for a userId OAFinder f = new OAFinder(Router.P_UserLogins); String cpp = UserLoginPP.user().userId().pp; f.addLikeFilter(cpp, userId); UserLogin userLogin = f.findFirst(router); OAFinder f = new OAFinder(ProgramPP.locations().employees().pp) { @Override protected boolean isUsed(Employee emp) { return super.isUsed(emp); } @Override protected void onFound(Employee emp) { //todo: } }; // f.setMaxFound(50); f.addEqualFilter(EmployeePP.employeeAward().awardType().name(), "some name"); ArrayList al = f.find(program); */ public class OAFinder { private String strPropertyPath; private OAPropertyPath propertyPath; private OALinkInfo liRecursiveRoot; private OALinkInfo[] linkInfos; private OALinkInfo[] recursiveLinkInfos; private Method[] methods; private boolean bAddOrFilter; private boolean bAddAndFilter; private OAFilter filter; private OACascade[] cascades; private volatile boolean bStop; private ArrayList alFound; // stack private boolean bEnableStack; private int stackPos; private StackValue[] stack; private int maxFound; private F fromObject; private Hub fromHub; private boolean bUseAll; private boolean bEnableRecursiveRoot; private boolean bEnableRecursiveRootWasCalled; private OACascade cascade; // cascade that can be set by calling code /** * flag to know if it should only find data that is currently in memory. */ private boolean bUseOnlyLoadedData; public OAFinder() { } public OAFinder(String propPath) { this.strPropertyPath = propPath; } public OAFinder(F fromObject, String propPath) { this.fromObject = fromObject; this.strPropertyPath = propPath; } public OAFinder(Hub fromHub, String propPath) { this(fromHub, propPath, true); } public OAFinder(Hub fromHub, String propPath, boolean bUseAll) { this.fromHub = fromHub; this.strPropertyPath = propPath; this.bUseAll = bUseAll; } public void setAllowRecursiveRoot(boolean b) { this.bEnableRecursiveRoot = b; this.bEnableRecursiveRootWasCalled = true; } /** * Flag to know if the root object/hub should allow for recursive duing the find. Default is determined by the following. Default is * false, unless hub is the root and it has a masterObject. */ public boolean getAllowRecursiveRoot() { return this.bEnableRecursiveRoot; } /** * Add the found object to the list that is returned by find. This can be overwritten to get all of the objects as they are found. * * @see stop to be able to have the find stop searching. */ protected void onFound(T obj) { alFound.add(obj); if (maxFound > 0 && alFound.size() >= maxFound) { stop(); } } /** * Called during a find when data was not found. Use stop() to have the find aborted. */ protected void onDataNotFound() { } /** * This is used to stop the current find that is in process. This can be used when overwriting the onFound(). */ public void stop() { bStop = true; } public boolean getStop() { return bStop; } public void setUseOnlyLoadedData(boolean b) { this.bUseOnlyLoadedData = b; } // 20160306 /** * Flag (default=false) to only use data that is already in memory and not to load from server or datasource. */ public boolean getUseOnlyLoadedData() { return bUseOnlyLoadedData; } public void setMaxFound(int x) { this.maxFound = x; } public int getMaxFound() { return this.maxFound; } public ArrayList find() { if (fromObject != null) { return find(fromObject); } if (fromHub != null) { if (bUseAll) { return find(fromHub); } F obj = fromHub.getAO(); if (obj != null) { return find(obj); } } return null; } public void setRoot(F obj) { this.fromObject = obj; } public void setRoot(Hub hub) { this.fromHub = hub; } /** * Given the propertyPath, find all of the objects from a Hub. */ public ArrayList find(Hub hubRoot) { ArrayList al = find(hubRoot, null); return al; } public ArrayList find(ArrayList alRoot) { return find(alRoot, null); } public ArrayList find(ArrayList alRoot, F objectLastUsed) { if (!bEnableRecursiveRootWasCalled) { bEnableRecursiveRoot = false; } alFound = new ArrayList(); if (bEnableStack) { stack = new StackValue[5]; } if (alRoot == null) { return alFound; } int x = alRoot.size(); if (x == 0) { return alFound; } F sample = alRoot.get(0); bStop = false; setup(sample.getClass()); int pos; if (objectLastUsed == null) { pos = 0; } else { pos = alRoot.indexOf(objectLastUsed) + 1; } for (; pos < x; pos++) { F objectRoot = alRoot.get(pos); if (objectRoot == null) { continue; } stackPos = 0; performFind(objectRoot); if (bStop) { break; } } ArrayList al = alFound; this.alFound = null; this.stack = null; this.stackPos = 0; this.cascades = null; return al; } // 20171224 update threadloc.getDetail public ArrayList find(Hub hubRoot, F objectLastUsed) { if (!bEnableRecursiveRootWasCalled) { if (hubRoot != null) { OALinkInfo li = HubDetailDelegate.getLinkInfoFromMasterObjectToDetail(hubRoot); if (li != null && li.getRecursive()) { bEnableRecursiveRoot = true; } } else { bEnableRecursiveRoot = true; } } ArrayList al = null; OASiblingHelper siblingHelper = null; if (!bUseOnlyLoadedData) { siblingHelper = new OASiblingHelper(hubRoot); siblingHelper.add(strPropertyPath); OAThreadLocalDelegate.addSiblingHelper(siblingHelper); } try { al = _find(hubRoot, objectLastUsed); } finally { if (siblingHelper != null) { OAThreadLocalDelegate.removeSiblingHelper(siblingHelper); } } return al; } private int rootHubPos; public int getRootHubPos() { return this.rootHubPos; } /** * Given the propertyPath, find all of the objects from a Hub, starting after objectLastFound */ protected ArrayList _find(Hub hubRoot, F objectLastUsed) { alFound = new ArrayList(); if (bEnableStack) { stack = new StackValue[5]; } if (hubRoot == null) { return alFound; } rootHubPos = -1; bStop = false; setup(hubRoot.getObjectClass()); if (objectLastUsed == null) { rootHubPos = 0; } else { rootHubPos = hubRoot.getPos(objectLastUsed) + 1; } for (;; rootHubPos++) { F objectRoot = hubRoot.getAt(rootHubPos); if (objectRoot == null) { break; } stackPos = 0; performFind(objectRoot); if (bStop) { break; } } ArrayList al = alFound; this.alFound = null; this.stack = null; this.stackPos = 0; cascades = null; return al; } public void clearFilters() { filter = null; } public void addFilter(OAFilter filter) { if (this.filter == null) { this.filter = filter; } else { if (bAddOrFilter) { this.filter = new OAOrFilter(this.filter, filter); } else { this.filter = new OAAndFilter(this.filter, filter); } } bAddAndFilter = bAddOrFilter = false; } public OAFilter getFilter() { return this.filter; } public void setFilter(OAFilter f) { this.filter = f; } /** * Returns true if a matching value is found. */ public boolean canFindFirst(F objectRoot) { int holdMax = getMaxFound(); setMaxFound(1); ArrayList al = find(objectRoot); if (getMaxFound() == 1) { setMaxFound(holdMax); } return (al != null && al.size() > 0); } public T findFirst() { if (fromObject != null) { return findFirst(fromObject); } if (fromHub != null) { if (bUseAll) { return findFirst(fromHub); } F obj = fromHub.getAO(); if (obj != null) { return findFirst(obj); } } return null; } /** * Finds the first matching value. If searching for a null, then this would return a null, so use the canFindFirst method instead. */ public T findFirst(F objectRoot) { if (objectRoot == null) { return null; } int holdMax = getMaxFound(); setMaxFound(1); ArrayList al = find(objectRoot); T obj; if (al != null && al.size() > 0) { obj = al.get(0); } else { obj = null; } if (getMaxFound() == 1) { setMaxFound(holdMax); } return obj; } public T findFirst(Hub hub) { int holdMax = getMaxFound(); setMaxFound(1); ArrayList al = find(hub); T obj; if (al.size() > 0) { obj = al.get(0); } else { obj = null; } if (getMaxFound() == 1) { setMaxFound(holdMax); } return obj; } public T findNext(Hub hub, F objectLastUsed) { int holdMax = getMaxFound(); setMaxFound(1); ArrayList al = find(hub, objectLastUsed); T obj; if (al.size() > 0) { obj = al.get(0); } else { obj = null; } if (getMaxFound() == 1) { setMaxFound(holdMax); } return obj; } public T findLast() { ArrayList al = find(); if (al == null) return null; int x = al.size(); if (x == 0) return null; return al.get(x-1); } public T findLast(F objectRoot) { ArrayList al = find(objectRoot); if (al == null) return null; int x = al.size(); if (x == 0) return null; return al.get(x-1); } public T findLast(Hub hub) { ArrayList al = find(hub); if (al == null) return null; int x = al.size(); if (x == 0) return null; return al.get(x-1); } static protected abstract class CompareFilter implements OAFilter { final String pp; Object objFound; Object valueFound; boolean bCancel; public CompareFilter(final String pp) { this.pp = pp; } @Override public boolean isUsed(final Object obj) { if (bCancel) return true; if (!(obj instanceof OAObject)) return false; Object objValue = ((OAObject)obj).getProperty(pp); if (objFound == null) { objFound = obj; valueFound = objValue; return true; } int x = OACompare.compare(objValue, valueFound); if (isCompareUsed(x)) { objFound = obj; valueFound = objValue; return true; } return false; } protected void cancel() { this.bCancel = true; } abstract boolean isCompareUsed(int compareValue); } public T findLargest(final String pp) { CompareFilter cf = new CompareFilter(pp) { @Override boolean isCompareUsed(int compareValue) { return compareValue >= 0; } }; addFilter(cf); T t = findLast(); cf.cancel(); return t; } public T findLargest(F objectRoot, String pp) { CompareFilter cf = new CompareFilter(pp) { @Override boolean isCompareUsed(int compareValue) { return compareValue >= 0; } }; addFilter(cf); T t = findLast(objectRoot); cf.cancel(); return t; } public T findLargest(Hub hub, String pp) { CompareFilter cf = new CompareFilter(pp) { @Override boolean isCompareUsed(int compareValue) { return compareValue >= 0; } }; addFilter(cf); T t = findLast(hub); cf.cancel(); return t; } public T findSmallest(final String pp) { CompareFilter cf = new CompareFilter(pp) { @Override boolean isCompareUsed(int compareValue) { return compareValue <= 0; } }; addFilter(cf); T t = findLast(); cf.cancel(); return t; } public T findSmallest(F objectRoot, String pp) { CompareFilter cf = new CompareFilter(pp) { @Override boolean isCompareUsed(int compareValue) { return compareValue <= 0; } }; addFilter(cf); T t = findLast(objectRoot); cf.cancel(); return t; } public T findSmallest(Hub hub, String pp) { CompareFilter cf = new CompareFilter(pp) { @Override boolean isCompareUsed(int compareValue) { return compareValue <= 0; } }; addFilter(cf); T t = findLast(hub); cf.cancel(); return t; } static protected class DuplicateFilter implements OAFilter { final String pp; boolean bCancel; final Map hm = new HashMap<>(); final Map hm2 = new HashMap<>(); public DuplicateFilter(final String pp) { this.pp = pp; } @Override public boolean isUsed(final Object obj) { if (bCancel) return true; if (!(obj instanceof OAObject)) return false; Object objValue = ((OAObject)obj).getProperty(pp); if (objValue == null) return false; OAObject objx = hm.get(objValue); if (objx != null) { hm2.put(objValue, objx); return true; } else { hm.put(objValue, (OAObject) obj); } return false; } protected void cancel() { this.bCancel = true; } } /** * Returns all objects that have another object with a duplicate value for a property (path).
* Note: null values are not included. */ public ArrayList findDuplicates(F objectRoot, String pp) { DuplicateFilter f = new DuplicateFilter(pp); addFilter(f); ArrayList al = find(objectRoot); f.cancel(); f.hm.clear(); for (Map.Entry entry : f.hm2.entrySet()) { al.add((T) entry.getValue()); } f.hm2.clear(); return al; } /** * Given the propertyPath, find all of the objects from a root object. * * @param objectRoot starting object to begin navigating through the propertyPath. */ public ArrayList find(F objectRoot) { if (objectRoot == null) { return null; } alFound = new ArrayList(); if (bEnableStack) { stack = new StackValue[5]; } stackPos = 0; if (objectRoot == null) { return alFound; } bStop = false; setup(objectRoot.getClass()); performFind(objectRoot); ArrayList al = alFound; this.alFound = null; this.stack = null; this.stackPos = 0; this.cascades = null; return al; } public OAPropertyPath getPropertyPath() { return this.propertyPath; } private boolean bSetup; protected void setup(Class c) { if (bSetup) { return; } bSetup = true; if (propertyPath != null || c == null) { return; } propertyPath = new OAPropertyPath(c, strPropertyPath, false); linkInfos = propertyPath.getLinkInfos(); recursiveLinkInfos = propertyPath.getRecursiveLinkInfos(); methods = propertyPath.getMethods(); int x = linkInfos == null ? 0 : linkInfos.length; if (x < methods.length) { // oafinder is to get from one OAObj/Hub to another, not a property/etc throw new RuntimeException("propertyPath " + strPropertyPath + " must end in an OAObject/Hub"); } cascades = new OACascade[linkInfos.length]; for (int i = 0; i < linkInfos.length; i++) { cascades[i] = new OACascade(); } OAObjectInfo oi = OAObjectInfoDelegate.getObjectInfo(c); liRecursiveRoot = oi.getRecursiveLinkInfo(OALinkInfo.MANY); if (liRecursiveRoot != null && linkInfos != null && linkInfos.length > 0) { if (linkInfos[0].getType() == OALinkInfo.ONE && linkInfos[0].getReverseLinkInfo().getType() == OALinkInfo.MANY && !linkInfos[0].getReverseLinkInfo().getRecursive()) { liRecursiveRoot = null; } } // match filters String[] names = propertyPath.getFilterNames(); Object[][] values = propertyPath.getFilterParamValues(); Constructor[] constructors = propertyPath.getFilterConstructors(); // (hub, hub, [params ...]) x = names.length; for (int i = 0; i < x; i++) { if (names[i] == null) { continue; } if (constructors[i] == null) { continue; } try { HubFilter hubFilter = createHubFilter(names[i]); if (hubFilter == null) { // hubFilter constructor that PP finds needs to have 2 hub params as first 2 params int xx = values[i] == null ? 0 : values[i].length; Object[] objs = new Object[2 + xx]; objs[0] = null; objs[1] = null; if (xx > 0) { System.arraycopy(values[i], 0, objs, 2, xx); } hubFilter = ((CustomHubFilter) constructors[i].newInstance(objs)).getHubFilter(); } if (filter == null) { filter = hubFilter; } else { filter = new OAAndFilter(filter, hubFilter); } } catch (Exception e) { throw new IllegalArgumentException("Filter " + names[i] + " can not be created", e); } } } private void performFind(F obj) { if (obj == null) { return; } find(obj, 0); } protected void find(Object obj, int pos) { if (obj == null || bStop) { return; } if (pos > 20) { return; } try { if (bEnableStack) { push(obj, pos); } _find(obj, pos); } finally { if (bEnableStack) { pop(); } } } private void _find(Object obj, int pos) { if (obj == null) { return; } if (obj instanceof Hub) { for (Object objx : (Hub) obj) { find(objx, pos); if (bStop) { break; } } return; } if (!(obj instanceof OAObject)) { return; } if (cascade != null) { boolean b = cascade.wasCascaded((OAObject) obj, true); if (b) { return; } } if (pos > 0 && cascades != null) { boolean b = cascades[pos - 1].wasCascaded((OAObject) obj, true); if (b) { return; } } if (linkInfos == null || pos >= linkInfos.length) { boolean bIsUsed; if (filter != null && !bStop) { bIsUsed = filter.isUsed(obj); } else { bIsUsed = true; } bIsUsed = !bStop && bIsUsed && isUsed((T) obj); if (bIsUsed) { onFound((T) obj); } if (bStop) { return; } } // check if recursive if (pos == 0) { // see if root object is recursive if (bEnableRecursiveRoot && liRecursiveRoot != null) { if (getUseOnlyLoadedData()) { if (!liRecursiveRoot.isLoaded(obj)) { onDataNotFound(); return; } /* 20180606 all will be sorted, since they are same li if ((obj instanceof OAObject) && getNeedsToBeSorted((OAObject) obj, liRecursiveRoot)) { onDataNotFound(); return; } */ } Object objx = liRecursiveRoot.getValue(obj); find(objx, pos); // go up a level to then go through hub if (bStop) { return; } } } else if (recursiveLinkInfos != null && pos <= recursiveLinkInfos.length) { if (recursiveLinkInfos[pos - 1] != null) { if (getUseOnlyLoadedData()) { if (!recursiveLinkInfos[pos - 1].isLoaded(obj)) { onDataNotFound(); return; } /* 20180606 all will be sorted, since they are same li if ((obj instanceof OAObject) && getNeedsToBeSorted((OAObject) obj, recursiveLinkInfos[pos - 1])) { onDataNotFound(); return; } */ } Object objx = recursiveLinkInfos[pos - 1].getValue(obj); find(objx, pos); if (bStop) { return; } } } if (linkInfos != null && pos < linkInfos.length) { if (getUseOnlyLoadedData()) { // 20180713 check if it needs to be sorted, and if a sortListener already created boolean b = linkInfos[pos].isLoaded(obj); if (b && linkInfos[pos].getType() == OALinkInfo.TYPE_MANY) { if (OAString.isNotEmpty(linkInfos[pos].getSortProperty())) { Object objx; if (linkInfos[pos].getCalculated()) { objx = linkInfos[pos].getValue((OAObject) obj); } else { objx = OAObjectPropertyDelegate.getProperty((OAObject) obj, linkInfos[pos].getName()); } if (objx instanceof Hub) { Hub h = (Hub) objx; if (HubSortDelegate.getSortListener(h) == null && HubDelegate.getAutoSequence(h) == null) { OAThreadLocal tl = OAThreadLocalDelegate.getThreadLocal(true); if (tl.cntGetSiblingCalled > 1) { b = false; } } } } } if (!b) { onDataNotFound(); return; } /* 20180606 might need to put this back in if ((obj instanceof OAObject) && getNeedsToBeSorted((OAObject) obj, linkInfos[pos])) { onDataNotFound(); return; } */ } Object objx = linkInfos[pos].getValue(obj); find(objx, pos + 1); if (bStop) { return; } } } private boolean getNeedsToBeSorted(OAObject obj, OALinkInfo li) { if (obj == null || li == null) { return false; } if (li.type != OALinkInfo.MANY) { return false; } if (OAString.isEmpty(li.getSortProperty())) { return false; } Hub hx; if (li.getCalculated()) { hx = (Hub) li.getValue((OAObject) obj); } else { hx = (Hub) OAObjectPropertyDelegate.getProperty((OAObject) obj, li.name, false, true); } if (hx == null || (HubSortDelegate.getSortListener(hx) == null && HubDelegate.getAutoSequence(hx) == null)) { return true; } return false; } /** * This will be called to create a filter that is in the propertyPaths. * * @param name name of the filter in the propertyPath */ protected HubFilter createHubFilter(String name) { return null; } /** * Called for all objects that are found, and pass the filter. * * @return true (default) to include in arrayList results, false to skip. */ protected boolean isUsed(T obj) { return true; } /** * This will have the internal stack updated when a find is being performed. * * @param b, default is false */ public void setEnabledStack(boolean b) { bEnableStack = b; } // used to keep track of the objects in the stack static class StackValue { Object obj; int pos; StackValue(Object obj, int pos) { this.obj = obj; this.pos = pos; } } private void push(Object obj, int pos) { StackValue sv = new StackValue(obj, pos); push(sv); } private void push(StackValue sv) { if (sv == null) { return; } int x = stack.length; if (stackPos == x) { StackValue[] temp = new StackValue[x + 10]; System.arraycopy(stack, 0, temp, 0, x); stack = temp; } stack[stackPos++] = sv; } private StackValue pop() { if (stackPos == 0) { return null; } StackValue sv = stack[--stackPos]; stack[stackPos] = null; return sv; } /** * The objects that are in the current stack. This can be used when overwriting the onFound(..) to know the object path. * * @see #setEnabledStack(boolean) to enable this information. */ public Object[] getStackObjects() { Object[] objs = new Object[stackPos]; for (int i = 0; i < stackPos; i++) { objs[i] = stack[i].obj; } return objs; } /** * The property name of the objects that are in the current stack. * * @see #setEnabledStack(boolean) to enable this information. */ public String[] getStackPropertyNames() { String[] ss = new String[stackPos]; for (int i = 0; i < stackPos; i++) { String methodName; if (stack[i].pos == 0) { methodName = "[root]"; } else if (stack[i].pos <= linkInfos.length) { methodName = linkInfos[stack[i].pos - 1].getName(); } else { methodName = methods[stack[i - 1].pos].getName(); } ss[i] = methodName; } return ss; } public void addBetweenFilter(String pp, Object val1, Object val2) { addFilter(new OABetweenFilter(pp, val1, val2)); } public void addBetweenOrEqualFilter(String pp, Object val1, Object val2) { addFilter(new OABetweenOrEqualFilter(pp, val1, val2)); } public void addEmptyFilter(String pp) { addFilter(new OAEmptyFilter(pp)); } public void addNotEmptyFilter(String pp) { addFilter(new OANotEmptyFilter(pp)); } public void addQueryFilter(Class c, String pp) { addFilter(new OAQueryFilter(c, pp)); } public void addQueryFilter(Class c, String query, Object[] args) { addFilter(new OAQueryFilter(c, query, args)); } public void addEqualFilter(String pp, Object val) { addFilter(new OAEqualFilter(pp, val)); } public void addEqualFilter(String pp, Object matchValue, boolean bIgnoreCase) { OAEqualFilter f = new OAEqualFilter(pp, matchValue); f.setIgnoreCase(bIgnoreCase); addFilter(f); } public void addEqualFilter(String pp, Object matchValue, int decimalPlaces) { OAEqualFilter f = new OAEqualFilter(pp, matchValue, decimalPlaces); addFilter(f); } public void addTrueFilter(String pp) { addFilter(new OAEqualFilter(pp, Boolean.TRUE)); } public void addFalseFilter(String pp) { addFilter(new OAEqualFilter(pp, Boolean.FALSE)); } public void addNullFilter(String pp) { addFilter(new OANullFilter(pp)); } public void addNotNullFilter(String pp) { addFilter(new OANotNullFilter(pp)); } public void addGreaterFilter(String pp, Object val) { addFilter(new OAGreaterFilter(pp, val)); } public void addGreaterOrEqualFilter(String pp, Object val) { addFilter(new OAGreaterOrEqualFilter(pp, val)); } public void addLessFilter(String pp, Object val) { addFilter(new OALessFilter(pp, val)); } public void addLessOrEqualFilter(String pp, Object val) { addFilter(new OALessOrEqualFilter(pp, val)); } public void addLikeFilter(String pp, Object val) { addFilter(new OALikeFilter(pp, val)); } public void addNotLikeFilter(String pp, Object val) { addFilter(new OANotLikeFilter(pp, val)); } public void addOrFilter(OAFilter f1, OAFilter f2) { OAOrFilter f = new OAOrFilter(f1, f2); addFilter(f); } /** * This will create an Or with the existing filter and the next filter that is added. */ public void addOrFilter() { bAddOrFilter = true; bAddAndFilter = false; } /** * This will create an And with the existing filter and the next filter that is added. */ public void addAndFilter() { bAddAndFilter = true; bAddOrFilter = false; } public void setCascade(OACascade cascade) { this.cascade = cascade; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy