/* Copyright 2009-2015 David Hadka
*
* This file is part of the MOEA Framework.
*
* The MOEA Framework is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* The MOEA Framework is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>.
*/
package org.moeaframework.analysis.collector;
import java.util.Stack;
/**
* Identifies objects in an object graph (all public and private fields
* contained within an object and those it references). For instance,
* <pre>
* AttachPoint.isClass(TypeA.class).and(AttachPoint.isNestedIn(TypeB.class)
* </pre>
* will match any objects of type {@code TypeA} that are referenced (or
* accessible through) an object of type {@code TypeB}.
*/
public abstract class AttachPoint {
/**
* Protected constructor to prevent instantiation, but allow subclassing.
*/
protected AttachPoint() {
super();
}
/**
* Returns an attach point which performs the logical AND between two
* attach points, matching an object only if both attach points match the
* object.
*
* @param that the second attach point
* @return an attach point which performs the logical AND between two
* attach points
*/
public AttachPoint and(final AttachPoint that) {
return new AttachPoint() {
@Override
public boolean matches(Stack<Object> parents, Object object) {
//note that AttachPoint.this points to the parent class, and
//not this anonymous class (i.e., A in A.and(B))
return AttachPoint.this.matches(parents, object) &&
that.matches(parents, object);
}
};
}
/**
* Returns an attach point which performs the logical OR between two
* attach points, matching an object if either or both of the attach points
* match the object.
*
* @param that the second attach point
* @return an attach point which performs the logical OR between two
* attach points
*/
public AttachPoint or(final AttachPoint that) {
return new AttachPoint() {
@Override
public boolean matches(Stack<Object> parents, Object object) {
//note that AttachPoint.this points to the parent class, and
//not this anonymous class (i.e., A in A.or(B))
return AttachPoint.this.matches(parents, object) ||
that.matches(parents, object);
}
};
}
/**
* Returns an attach point which performs the logical NOT on the given
* attach point, matching an object only if the attach point does not match
* the object.
*
* @param that the original attach point
* @return an attach point which performs the logical NOT on the given
* attach point
*/
public static AttachPoint not(final AttachPoint that) {
return new AttachPoint() {
@Override
public boolean matches(Stack<Object> parents, Object object) {
return !that.matches(parents, object);
}
};
}
/**
* Returns an attach point that matches an object if its type is equal to
* the specified type. Note that subclasses of the type will not match.
*
* @param type the required type of the object
* @return an attach point that matches an object if its type is equal to
* the specified type
*/
public static AttachPoint isClass(final Class<?> type) {
return new AttachPoint() {
@Override
public boolean matches(Stack<Object> parents, Object object) {
return object.getClass().equals(type);
}
};
}
/**
* Returns an attach point that matches an object if it is an instance of
* the specified type. The specified type and all subclasses will match.
*
* @param type the required type/supertype of the object
* @return an attach point that matches an object if it is an instance of
* the specified type
*/
public static AttachPoint isSubclass(final Class<?> type) {
return new AttachPoint() {
@Override
public boolean matches(Stack<Object> parents, Object object) {
return type.isInstance(object);
}
};
}
/**
* Returns an attach point that matches an object if it is a declared field
* of the specified type.
*
* @param parentType the parent type
* @return an attach point that matches an object if it is a declared field
* of the specified type
*/
public static AttachPoint isDeclaredIn(final Class<?> parentType) {
return new AttachPoint() {
@Override
public boolean matches(Stack<Object> parents, Object object) {
if (parents.isEmpty()) {
return false;
} else {
return parentType.isInstance(parents.peek());
}
}
};
}
/**
* Returns an attach point that matches an object if it is nested inside a
* class of the specified type. This includes both declared fields of the
* specified type and all fields accessible by recursively following all
* references.
*
* @param ancestorType the ancestor type
* @return an attach point that matches an object if it is nested inside a
* class of the specified type
*/
public static AttachPoint isNestedIn(final Class<?> ancestorType) {
return new AttachPoint() {
@Override
public boolean matches(Stack<Object> parents, Object object) {
for (Object parent : parents) {
if (ancestorType.isInstance(parent)) {
return true;
}
}
return false;
}
};
}
/**
* Returns {@code true} if the specified object in the object graph matches
* some criteria; and {@code false} otherwise.
*
* @param parents the path from the root (starting) object to the current
* object
* @param object the current object
* @return {@code true} if the specified object in the object graph matches
* some criteria; and {@code false} otherwise
*/
public abstract boolean matches(Stack<Object> parents, Object object);
}