/* Copyright (2006-2012) Schibsted ASA * This file is part of Possom. * * Possom 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. * * Possom 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 Possom. If not, see <http://www.gnu.org/licenses/>. */ package no.sesat.search.query.finder; import no.sesat.commons.visitor.AbstractReflectionVisitor; import java.util.ArrayList; import java.util.Collections; import java.util.List; import no.sesat.search.query.BinaryClause; import no.sesat.search.query.LeafClause; import no.sesat.search.query.UnaryClause; import no.sesat.search.query.XorClause; import no.sesat.search.query.parser.*; import org.apache.log4j.Logger; /** * * * @version $Id$ */ public final class ForestFinder extends AbstractReflectionVisitor { private static final Logger LOG = Logger.getLogger(ForestFinder.class); private static final String DEBUG_COUNT_TO = " trees in forest "; private boolean searching = false; private final List<BinaryClause> roots = new ArrayList<BinaryClause>(); private static final String ERR_CANNOT_CALL_VISIT_DIRECTLY = "visit(object) can't be called directly on this visitor!"; /** * * @param root * @return */ public synchronized List<BinaryClause> findForestRoots(final UnaryClause root) { if (searching) { throw new IllegalStateException(ERR_CANNOT_CALL_VISIT_DIRECTLY); } searching = true; roots.clear(); visit(root); searching = false; return Collections.unmodifiableList(new ArrayList<BinaryClause>(roots)); } /** * * @param clause */ protected void visitImpl(final UnaryClause clause) { clause.getFirstClause().accept(this); } /** * * @param clause */ protected void visitImpl(final XorClause clause) { clause.getFirstClause().accept(this); clause.getSecondClause().accept(this); } /** * * @param clause */ protected void visitImpl(final BinaryClause clause) { final BinaryClause forestDepth = forestWalk(clause); clause.getFirstClause().accept(this); forestDepth.getSecondClause().accept(this); } /** * * @param clause */ protected void visitImpl(final LeafClause clause) { // leaves can't be forest roots :-) } /** Returns the deepest tree in the forest. * And adds the forest to the roots if it contains more than one tree. **/ private <T extends BinaryClause> T forestWalk(final T clause){ int count = 1; T forestDepth = clause; // presumption below is that forests can't mix implementation classes, not just interfaces. for (; forestDepth.getSecondClause().getClass() == clause.getClass(); forestDepth = (T) forestDepth.getSecondClause()){ ++count; } LOG.debug(count + DEBUG_COUNT_TO + clause); if(count >1){ roots.add(clause); } return forestDepth; } }