/*
* Copyright (C) 2011 Laurent Caillette
*
* This program 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.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.treemangling.designator;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang.StringUtils;
import org.novelang.common.Problem;
import org.novelang.common.SyntacticTree;
import org.novelang.common.tree.RobustPath;
import org.novelang.common.tree.Treepath;
import org.novelang.designator.FragmentIdentifier;
/**
* This class creates maps from {@link FragmentIdentifier} to {@link Treepath} objects
* through {@link RobustPath}.
*
* @see org.novelang.treemangling.DesignatorInterpreter
*
* @author Laurent Caillette
*/
public class BabyInterpreter implements FragmentMapper< RobustPath< SyntacticTree > > {
/**
* Contain only pure identifiers (defined explicitely).
*/
private final Map< FragmentIdentifier, RobustPath< SyntacticTree > > pureIdentifiers ;
/**
* Contain implicit identifiers, or implicit identifiers mixed with explicit identifiers.
*/
private final Map< FragmentIdentifier, RobustPath< SyntacticTree > > derivedIdentifiers ;
private final List< Problem > problems ;
public BabyInterpreter( final Treepath< SyntacticTree > treepath ) {
final Collector collector = new Collector() ;
process( collector, treepath ) ;
pureIdentifiers = Collections.unmodifiableMap( collector.pureIdentifiers ) ;
for( final FragmentIdentifier duplicate : collector.duplicateDerivedIdentifiers ) {
collector.derivedIdentifiers.remove( duplicate ) ;
}
derivedIdentifiers = Collections.unmodifiableMap( collector.derivedIdentifiers ) ;
problems = Collections.unmodifiableList( collector.problems ) ;
}
/**
* Returns the map of pure identifiers (those made out only from
* {@link org.novelang.parser.NodeKind#ABSOLUTE_IDENTIFIER} nodes).
*
* @return a non-null, immutable map containing no nulls, with {@code Treepath} objects
* referencing the same tree as passed to the constructor.
*/
@Override
public Map< FragmentIdentifier, RobustPath< SyntacticTree > > getPureIdentifierMap() {
return pureIdentifiers ;
}
/**
* Returns the map of derived identifiers (those which are not pure).
*
* @return a non-null, immutable map containing no nulls, with {@code Treepath} objects
* referencing the same tree as passed to the constructor.
*/
@Override
public Map< FragmentIdentifier, RobustPath< SyntacticTree > > getDerivedIdentifierMap() {
return derivedIdentifiers ;
}
/**
* Returns problems like duplicate identifier.
*/
public Iterable< Problem > getProblems() {
return problems ;
}
public boolean hasProblem() {
return ! problems.isEmpty() ;
}
private void process(
final Collector collector,
final Treepath< SyntacticTree > treepath
) {
final SyntacticTree tree = treepath.getTreeAtEnd() ;
if( tree.isOneOf( DesignatorTools.IDENTIFIER_BEARING_NODEKINDS ) ) {
final FragmentIdentifier explicitIdentifier ;
final SegmentExtractor segmentExtractor =
new SegmentExtractor( treepath, collector.problems ) ;
final IdentifierDefinition definition = segmentExtractor.getIdentifierDefinition() ;
final String segment = segmentExtractor.getSegment() ;
switch( definition ) {
case NONE :
explicitIdentifier = null ;
break ;
case ABSOLUTE :
explicitIdentifier = new FragmentIdentifier( segment ) ;
if( verifyFreshness( collector, tree, explicitIdentifier, collector.pureIdentifiers ) ) {
collector.pureIdentifiers.put(
explicitIdentifier,
RobustPath.create( treepath, DesignatorTools.IDENTIFIER_TREE_FILTER )
) ;
}
break ;
case IMPLICIT :
if( ! StringUtils.isBlank( segment ) ) {
explicitIdentifier = null ;
final FragmentIdentifier implicitAbsoluteIdentifier = new FragmentIdentifier( segment ) ;
if( collector.derivedIdentifiers.containsKey( implicitAbsoluteIdentifier ) ) {
collector.duplicateDerivedIdentifiers.add( implicitAbsoluteIdentifier ) ;
} else {
collector.derivedIdentifiers.put(
implicitAbsoluteIdentifier,
RobustPath.create( treepath, DesignatorTools.IDENTIFIER_TREE_FILTER )
) ;
}
}
break ;
default :
throw new IllegalArgumentException( "Unsupported: " + definition ) ;
}
for( int i = 0 ; i < tree.getChildCount() ; i ++ ) {
process(
collector,
Treepath.create( treepath, i )
) ;
}
}
}
private boolean verifyFreshness(
final Collector collector,
final SyntacticTree tree,
final FragmentIdentifier fragmentIdentifier,
final Map< FragmentIdentifier, RobustPath< SyntacticTree > > map
) {
if( map.containsKey( fragmentIdentifier ) ) {
final String message = "Already defined: '" + fragmentIdentifier + "'" ;
addProblem( collector, tree, message ) ;
return false ;
}
return true ;
}
private void addProblem(
final Collector collector,
final SyntacticTree tree,
final String message
) {
if( tree.getLocation() == null ) {
// Only for tests.
collector.problems.add( Problem.createProblem( message ) ) ;
} else {
collector.problems.add( Problem.createProblem(
message,
tree.getLocation() )
) ;
}
}
private static class Collector {
/**
* Contain only pure identifiers (defined explicitely).
*/
public final Map< FragmentIdentifier, RobustPath< SyntacticTree > >
pureIdentifiers = Maps.newHashMap() ;
/**
* Contain implicit identifiers, or implicit identifiers mixed with explicit identifiers.
*/
public final Map< FragmentIdentifier, RobustPath< SyntacticTree > >
derivedIdentifiers = Maps.newHashMap() ;
public final Set< FragmentIdentifier > duplicateDerivedIdentifiers = Sets.newHashSet() ;
public final List< Problem > problems = Lists.newArrayList() ;
}
}