/******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ package cuchaz.enigma.convert; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import com.beust.jcommander.internal.Lists; import com.beust.jcommander.internal.Maps; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Multimap; public class ClassMatching { private Multimap m_sourceClasses; private Multimap m_matchedDestClasses; private List m_unmatchedDestClasses; public ClassMatching( ) { m_sourceClasses = ArrayListMultimap.create(); m_matchedDestClasses = ArrayListMultimap.create(); m_unmatchedDestClasses = Lists.newArrayList(); } public void addSource( ClassIdentity c ) { m_sourceClasses.put( c, c ); } public void matchDestClass( ClassIdentity destClass ) { Collection matchedSourceClasses = m_sourceClasses.get( destClass ); if( matchedSourceClasses.isEmpty() ) { // no match m_unmatchedDestClasses.add( destClass ); } else { // found a match m_matchedDestClasses.put( destClass, destClass ); // DEBUG ClassIdentity sourceClass = matchedSourceClasses.iterator().next(); assert( sourceClass.hashCode() == destClass.hashCode() ); assert( sourceClass.equals( destClass ) ); } } public void removeSource( ClassIdentity sourceClass ) { m_sourceClasses.remove( sourceClass, sourceClass ); } public void removeDest( ClassIdentity destClass ) { m_matchedDestClasses.remove( destClass, destClass ); m_unmatchedDestClasses.remove( destClass ); } public List getSourceClasses( ) { return new ArrayList( m_sourceClasses.values() ); } public List getDestClasses( ) { List classes = Lists.newArrayList(); classes.addAll( m_matchedDestClasses.values() ); classes.addAll( m_unmatchedDestClasses ); return classes; } public BiMap getUniqueMatches( ) { BiMap uniqueMatches = HashBiMap.create(); for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) { Collection matchedSourceClasses = m_sourceClasses.get( sourceClass ); Collection matchedDestClasses = m_matchedDestClasses.get( sourceClass ); if( matchedSourceClasses.size() == 1 && matchedDestClasses.size() == 1 ) { ClassIdentity matchedSourceClass = matchedSourceClasses.iterator().next(); ClassIdentity matchedDestClass = matchedDestClasses.iterator().next(); uniqueMatches.put( matchedSourceClass, matchedDestClass ); } } return uniqueMatches; } public BiMap,List> getAmbiguousMatches( ) { BiMap,List> ambiguousMatches = HashBiMap.create(); for( ClassIdentity sourceClass : m_sourceClasses.keySet() ) { Collection matchedSourceClasses = m_sourceClasses.get( sourceClass ); Collection matchedDestClasses = m_matchedDestClasses.get( sourceClass ); if( matchedSourceClasses.size() > 1 && matchedDestClasses.size() > 1 ) { ambiguousMatches.put( new ArrayList( matchedSourceClasses ), new ArrayList( matchedDestClasses ) ); } } return ambiguousMatches; } public int getNumAmbiguousSourceMatches( ) { int num = 0; for( Map.Entry,List> entry : getAmbiguousMatches().entrySet() ) { num += entry.getKey().size(); } return num; } public int getNumAmbiguousDestMatches( ) { int num = 0; for( Map.Entry,List> entry : getAmbiguousMatches().entrySet() ) { num += entry.getValue().size(); } return num; } public List getUnmatchedSourceClasses( ) { List classes = Lists.newArrayList(); for( ClassIdentity sourceClass : getSourceClasses() ) { if( m_matchedDestClasses.get( sourceClass ).isEmpty() ) { classes.add( sourceClass ); } } return classes; } public List getUnmatchedDestClasses( ) { return new ArrayList( m_unmatchedDestClasses ); } public Map>> getConversionMap( ) { Map>> conversion = Maps.newHashMap(); for( Map.Entry entry : getUniqueMatches().entrySet() ) { conversion.put( entry.getKey().getClassEntry().getName(), new AbstractMap.SimpleEntry>( entry.getKey(), Arrays.asList( entry.getValue() ) ) ); } for( Map.Entry,List> entry : getAmbiguousMatches().entrySet() ) { for( ClassIdentity sourceClass : entry.getKey() ) { conversion.put( sourceClass.getClassEntry().getName(), new AbstractMap.SimpleEntry>( sourceClass, entry.getValue() ) ); } } for( ClassIdentity sourceClass : getUnmatchedSourceClasses() ) { conversion.put( sourceClass.getClassEntry().getName(), new AbstractMap.SimpleEntry>( sourceClass, getUnmatchedDestClasses() ) ); } return conversion; } @Override public String toString( ) { StringBuilder buf = new StringBuilder(); buf.append( String.format( "%12s%8s%8s\n", "", "Source", "Dest" ) ); buf.append( String.format( "%12s%8d%8d\n", "Classes", getSourceClasses().size(), getDestClasses().size() ) ); buf.append( String.format( "%12s%8d%8d\n", "Unique", getUniqueMatches().size(), getUniqueMatches().size() ) ); buf.append( String.format( "%12s%8d%8d\n", "Ambiguous", getNumAmbiguousSourceMatches(), getNumAmbiguousDestMatches() ) ); buf.append( String.format( "%12s%8d%8d\n", "Unmatched", getUnmatchedSourceClasses().size(), getUnmatchedDestClasses().size() ) ); return buf.toString(); } }