/******************************************************************************* * 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.analysis; import java.util.List; import java.util.Map; import java.util.TreeMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.strobel.decompiler.languages.Region; import com.strobel.decompiler.languages.java.ast.Identifier; import cuchaz.enigma.mapping.Entry; public class SourceIndex { private String m_source; private TreeMap m_tokens; private Map m_declarations; private List m_lineOffsets; public SourceIndex( String source ) { m_source = source; m_tokens = Maps.newTreeMap(); m_declarations = Maps.newHashMap(); m_lineOffsets = Lists.newArrayList(); // count the lines m_lineOffsets.add( 0 ); for( int i=0; i= 0 ) { token.end -= pos + 1; } // HACKHACK: sometimes node regions are off by one // I think this is a bug in Procyon, but it's easy to work around if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) ) { token.start++; token.end++; if( !Character.isJavaIdentifierStart( m_source.charAt( token.start ) ) ) { throw new IllegalArgumentException( "Region " + region + " does not describe valid token: '" + m_source.substring( token.start, token.end ) + "'" ); } } return token; } public void add( Identifier node, Entry deobfEntry ) { Token token = getToken( node ); if( token != null ) { m_tokens.put( token, deobfEntry ); } } public void addDeclaration( Identifier node, Entry deobfEntry ) { Token token = getToken( node ); if( token != null ) { m_tokens.put( token, deobfEntry ); m_declarations.put( deobfEntry, token ); } } public Token getToken( int pos ) { Map.Entry mapEntry = m_tokens.floorEntry( new Token( pos, pos ) ); if( mapEntry == null ) { return null; } Token token = mapEntry.getKey(); if( token.contains( pos ) ) { return token; } return null; } public Entry getEntry( Token token ) { if( token == null ) { return null; } return m_tokens.get( token ); } public Iterable tokens( ) { return m_tokens.keySet(); } public Token getDeclarationToken( Entry deobfEntry ) { return m_declarations.get( deobfEntry ); } private int toPos( int line, int col ) { // line and col are 1-based return m_lineOffsets.get( line - 1 ) + col - 1; } }