001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.query;
022:
023: import java.io.*;
024: import java.lang.reflect.*;
025:
026: /**
027: * Base class for native queries.
028: * <br><br>Native Queries provide the ability to run one or more lines
029: * of code against all instances of a class. Native query expressions should
030: * return true to mark specific instances as part of the result set.
031: * db4o will attempt to optimize native query expressions and run them
032: * against indexes and without instantiating actual objects, where this is
033: * possible.<br><br>
034: * The syntax of the enclosing object for the native query expression varies
035: * slightly, depending on the language version used. Here are some examples,
036: * how a simple native query will look like in some of the programming languages and
037: * dialects that db4o supports:<br><br>
038: *
039: * <code>
040: * <b>// C# .NET 2.0</b><br>
041: * IList <Cat> cats = db.Query <Cat> (delegate(Cat cat) {<br>
042: *    return cat.Name == "Occam";<br>
043: * });<br>
044: * <br>
045: *<br>
046: * <b>// Java JDK 5</b><br>
047: * List <Cat> cats = db.query(new Predicate<Cat>() {<br>
048: *    public boolean match(Cat cat) {<br>
049: *       return cat.getName().equals("Occam");<br>
050: *    }<br>
051: * });<br>
052: * <br>
053: * <br>
054: * <b>// Java JDK 1.2 to 1.4</b><br>
055: * List cats = db.query(new Predicate() {<br>
056: *    public boolean match(Cat cat) {<br>
057: *       return cat.getName().equals("Occam");<br>
058: *    }<br>
059: * });<br>
060: * <br>
061: * <br>
062: * <b>// Java JDK 1.1</b><br>
063: * ObjectSet cats = db.query(new CatOccam());<br>
064: * <br>
065: * public static class CatOccam extends Predicate {<br>
066: *    public boolean match(Cat cat) {<br>
067: *       return cat.getName().equals("Occam");<br>
068: *    }<br>
069: * });<br>
070: * <br>
071: * <br>
072: * <b>// C# .NET 1.1</b><br>
073: * IList cats = db.Query(new CatOccam());<br>
074: * <br>
075: * public class CatOccam : Predicate {<br>
076: *    public boolean Match(Cat cat) {<br>
077: *       return cat.Name == "Occam";<br>
078: *    }<br>
079: * });<br>
080: * </code>
081: * <br>
082: * Summing up the above:<br>
083: * In order to run a Native Query, you can<br>
084: * - use the delegate notation for .NET 2.0.<br>
085: * - extend the Predicate class for all other language dialects<br><br>
086: * A class that extends Predicate is required to
087: * implement the #match() / #Match() method, following the native query
088: * conventions:<br>
089: * - The name of the method is "#match()" (Java) / "#Match()" (.NET).<br>
090: * - The method must be public public.<br>
091: * - The method returns a boolean.<br>
092: * - The method takes one parameter.<br>
093: * - The Type (.NET) / Class (Java) of the parameter specifies the extent.<br>
094: * - For all instances of the extent that are to be included into the
095: * resultset of the query, the match method should return true. For all
096: * instances that are not to be included, the match method should return
097: * false.<br><br>
098: */
099: public abstract class Predicate<ExtentType> implements Serializable {
100:
101: /**
102: * public for implementation reasons, please ignore.
103: */
104: public final static String PREDICATEMETHOD_NAME = "match";
105:
106: private Class<? extends ExtentType> _extentType;
107:
108: private transient Method cachedFilterMethod = null;
109:
110: public Predicate() {
111: this (null);
112: }
113:
114: public Predicate(Class<ExtentType> extentType) {
115: _extentType = extentType;
116: }
117:
118: private Method getFilterMethod() {
119: if (cachedFilterMethod != null) {
120: return cachedFilterMethod;
121: }
122: Method[] methods = getClass().getMethods();
123: for (int methodIdx = 0; methodIdx < methods.length; methodIdx++) {
124: Method method = methods[methodIdx];
125: if ((method.getName().equals(PREDICATEMETHOD_NAME))
126: && method.getParameterTypes().length == 1) {
127: String targetName = method.getParameterTypes()[0]
128: .getName();
129: if (!"java.lang.Object".equals(targetName)) {
130: cachedFilterMethod = method;
131: return method;
132: }
133: }
134: }
135: throw new IllegalArgumentException("Invalid predicate.");
136: }
137:
138: /**
139: * public for implementation reasons, please ignore.
140: */
141: public Class<? extends ExtentType> extentType() {
142: if (_extentType != null) {
143: return _extentType;
144: }
145: Class<ExtentType> extentType = (Class<ExtentType>) getFilterMethod()
146: .getParameterTypes()[0];
147: try {
148: Type genericType = ((ParameterizedType) getClass()
149: .getGenericSuperclass()).getActualTypeArguments()[0];
150: if ((genericType instanceof Class)
151: && (extentType
152: .isAssignableFrom((Class) genericType))) {
153: extentType = (Class<ExtentType>) genericType;
154: }
155: } catch (RuntimeException e) {
156: }
157: return extentType;
158: }
159:
160: /**
161: * The match method that needs to be implemented by the user.
162: * @param candidate the candidate object passed from db4o
163: * @return true to include an object in the resulting ObjectSet
164: */
165: public abstract boolean match(ExtentType candidate);
166:
167: /**
168: * public for implementation reasons, please ignore.
169: */
170: public boolean appliesTo(ExtentType candidate) {
171: try {
172: Method filterMethod = getFilterMethod();
173: filterMethod.setAccessible(true);
174: Object ret = filterMethod.invoke(this ,
175: new Object[] { candidate });
176: return ((Boolean) ret).booleanValue();
177: } catch (Exception e) {
178: e.printStackTrace();
179: return false;
180: }
181: }
182: }
|