/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2002 * Copyright by ESO (in the framework of the ALMA collaboration), * All rights reserved * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package alma.acs.entityutil; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Enumeration; import alma.acs.util.StopWatch; import alma.entities.commonentity.EntityRefT; /** * Takes an entity object (binding class) and traverses the tree to find * references ({@link EntityRefT} nodes) to other entity objects. * <p> * Currently specific to XML binding classes produced by the Castor framework; * should be not too hard though to adapt to others (mainly enumeration stuff) * <p> * Assumes that it's a tree, not a graph * (therefore no checks whether a node has been visited already). * On a graph this could lead to an infinite loop. * <p> * Possible optimization would be to compute on demand a list of Class objects for * which we know that they can't have EntityRefT children, and to * subsequently stop the recursion there. * * @author hsommer Apr 24, 2003 2:05:43 PM */ public class EntityRefFinder { private boolean m_debug; /** * */ public EntityRefFinder() { this(false); } public EntityRefFinder(boolean debug) { super(); m_debug = debug; } public EntityRefT[] findEntityReferences(Object rootEntityObject) throws EntityException { ArrayList<EntityRefT> entityRefs = new ArrayList<EntityRefT>(); StopWatch stopw = new StopWatch(null); recursiveFindEntityReferences(rootEntityObject, entityRefs); if (m_debug) { System.out.println("findEntityReferences took " + stopw.getLapTimeMillis() + " ms."); } return ( entityRefs.toArray(new EntityRefT[0]) ); } private void recursiveFindEntityReferences(Object obj, ArrayList<EntityRefT> entityRefs) throws EntityException { try { // dead end cases if (obj == null) { return; } Class objClass = obj.getClass(); if (obj instanceof Class || objClass.isPrimitive() || objClass.isArray() || objClass.getPackage().getName().equals("java.lang") ) { return; } if (m_debug) { System.out.println(obj.getClass().getName()); } // the good recursion stopper case if (obj instanceof EntityRefT) { entityRefs.add((EntityRefT)obj); return; } // recursion for child objects Method[] methods = obj.getClass().getMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().startsWith("get") && methods[i].getParameterTypes().length == 0) { Object retObj = methods[i].invoke(obj, (Object[]) null); recursiveFindEntityReferences(retObj, entityRefs); } else if (methods[i].getName().startsWith("enumerate") && methods[i].getReturnType().isAssignableFrom(Enumeration.class) && methods[i].getParameterTypes().length == 0 && !Modifier.isStatic(methods[i].getModifiers()) // Castor enum classes have a static enumerate() method... ) { Enumeration retObjEnum = (Enumeration) methods[i].invoke(obj, (Object[]) null); while (retObjEnum.hasMoreElements()) { Object retObj = retObjEnum.nextElement(); recursiveFindEntityReferences(retObj, entityRefs); } } } } catch (EntityException ex) { // to avoid repeated wrapping when flying up the recursion stack throw ex; } catch (Throwable thr) { throw new EntityException("failed to find entity references ", thr); } } }