Package jd.ide.eclipse.realignment.editors

Source Code of jd.ide.eclipse.realignment.editors.DecompilerOutputUtil$InputLine

package jd.ide.eclipse.realignment.editors;

import java.util.ArrayList;
import java.util.List;

import jd.ide.eclipse.JavaDecompilerPlugin;
import jd.ide.eclipse.realignment.Prefs;

import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jface.preference.IPreferenceStore;


/**
* DecompilerOutputUtil
*
* @project Java Decompiler Eclipse Plugin
* @author  Alex Kosinsky
*
*/
public class DecompilerOutputUtil {

  /**
   * Input string
   */
  private final String input;

  /**
   * Input split into lines
   */
  private final List<InputLine> inputLines=new ArrayList<InputLine>();//


  /**
   * Parsed input
   */
  private CompilationUnit unit;

  /**
   * Output lines
   */
  private final List<JavaSrcLine> javaSrcLines=new ArrayList<JavaSrcLine> ();

  private final String line_separator = System.getProperty("line.separator","\r\n");
  private final int line_separator_len = line_separator.length();

  private class InputLine{
    String line;
    int outputLineNum=-1;
    int calculatedNumLineJavaSrc=-1;

    @Override
    public String toString() {
      return line;
    }
  }

  private class JavaSrcLine{
    List<Integer> inputLines=new ArrayList<Integer>();

    @Override
    public String toString() {
      return inputLines.toString();
    }
  }


  public DecompilerOutputUtil(String input){
    this.input=input+line_separator;
  }

  public char[] realign(){
    // Handle special cases
    if(input.length()==0)
      return input.toCharArray();
    if(input==null)
      return null;

    // Compute the string offset of every source line
    fillOutputList();

    // Parse source code into AST
    javaSrcLines.add(null);
    @SuppressWarnings("deprecation")
    ASTParser parser = ASTParser.newParser(AST.JLS3);
    parser.setSource(input.toCharArray());
    unit=(CompilationUnit)parser.createAST(null);

    // Iterate over types (ignoring enums and annotations)
    List<?> types=unit.types();
    for(int i=0;i<types.size();i++){
      if(types.get(i) instanceof AbstractTypeDeclaration)
      {
        // Recursively process the elements within this type
        processElements((AbstractTypeDeclaration)types.get(i));
      }
    }

    // Iterate over types (ignorning enums and annotations)
    int firstTypeLine=Integer.MAX_VALUE;
    int lastTypeLine=Integer.MIN_VALUE;
    for(int i=0;i<types.size();i++){
      if(!(types.get(i) instanceof AbstractTypeDeclaration))
        continue;
      AbstractTypeDeclaration type=(AbstractTypeDeclaration)types.get(i);

      // Recursively process the types within this type
      processTypes(type);

      // Update firstTypeLine/lastTypeLine
      int numLine=unit.getLineNumber(type.getStartPosition());
      if(numLine<firstTypeLine)
        firstTypeLine=numLine;
      numLine=unit.getLineNumber(type.getStartPosition()+type.getLength()-1);
      if(numLine>lastTypeLine)
        lastTypeLine=numLine;
    }

    // Special case - no source items to handle so just return our input
    if(javaSrcLines.size()==1)
    {
      String warning = "/* Warning: No line numbers available in class file */\n";
      return (warning + input).toCharArray();
    }

    // Add all the source lines above the first type
    if(firstTypeLine!=Integer.MAX_VALUE)
      addBelow(firstTypeLine-1, 0, 0);

    // Add all the source lines below the last type
    if(lastTypeLine!=Integer.MIN_VALUE)
      addBelow(inputLines.size()-2, lastTypeLine, javaSrcLines.size()-1);

    // Create aligned source
    return toString().toCharArray();
  }

  @Override
  public String toString() {

    IPreferenceStore prefStore = JavaDecompilerPlugin.getDefault().getPreferenceStore();
    boolean displayLineNums = prefStore.getBoolean(Prefs.PREF_DISPLAY_LINE_NUMBERS);

    String line;
    int numLine;
    StringBuffer realignOutput=new StringBuffer();

    for (int i = 1; i < javaSrcLines.size(); i++) {
      JavaSrcLine javaSrcLine = initJavaSrcListItem(i);

      if (javaSrcLine.inputLines.size() > 0) {
        for (int j = 0; j < javaSrcLine.inputLines.size(); j++) {
          numLine = javaSrcLine.inputLines.get(j);
          line = inputLines.get(numLine).line;
          line = line.substring(0, line.length() - line_separator_len);

          if (!displayLineNums) {
            int commentStart = line.indexOf("/*");
            int commentEnd = line.indexOf("*/ ");

            if ((commentStart == 0) && (commentEnd > -1)) {
              line = line.substring(commentEnd + 3);
            }
          }

          realignOutput.append(line);
        }
      } else if (displayLineNums) {
        realignOutput.append("/*     */");
      }

      realignOutput.append(line_separator);
    }
    return realignOutput.toString();
  }

  private void fillOutputList(){
    int lineStart=0;
    int lineEnd=0;
    inputLines.add(null);
    while(lineStart < input.length()){
      // Compute line end
      lineEnd=input.indexOf('\n', lineEnd);
      if(lineEnd==-1)
        lineEnd=input.length();
      else
        lineEnd++;

      // Build OutputLine object
      InputLine outputLine=new InputLine();
      outputLine.line=input.substring(lineStart, lineEnd);
      inputLines.add(outputLine);

      // Next line start is current line end
      lineStart = lineEnd;
    }
  }

  private int parseJavaLineNumber(String line){
    int p1,p2;
    p1=line.indexOf("/*",0);
    p2=line.indexOf("*/",p1);
    if(p1==0 && p2>0){
      try{
        return Integer.parseInt(line.substring(p1+2, p2-1).trim());
      }catch(Exception ex){}
    }
    return -1;
  }

  /**
   * Make sure {@link #javaSrcLines} is at least outputLineNum entries long (adding nulls
   * if necessary).
   *
   * @param outputLineNum
   * @return the {@link JavaSrcLine} at index outputLineNum (creating one if
   * one doesn't already exist).
   */
  private JavaSrcLine initJavaSrcListItem(int outputLineNum){

    // Fill in nulls for any previous output line nums which we haven't visited yet
    if(javaSrcLines.size()<=outputLineNum){
      for(int a=javaSrcLines.size();a<=outputLineNum;a++)
        javaSrcLines.add(null);
    }

    // Create an output entry at the outputLineNum index
    JavaSrcLine javaSrcLine=javaSrcLines.get(outputLineNum);
    if(javaSrcLine==null){
      javaSrcLine=new JavaSrcLine();
      javaSrcLines.set(outputLineNum,javaSrcLine);
    }
    return javaSrcLine;
  }

  private void addAbove(int inputBeginLineNo,int inputLineNo,int outputLineNo){
    if(outputLineNo==1)
      return;

    int offset=1;

    /* Example:
     *
     * 19: /     /   public static boolean isTranslucencySupported(Translucency paramTranslucency)
     * 20: /     /   {
     * 21: / 105 /     switch (1.$SwitchMap$com$sun$awt$AWTUtilities$Translucency[paramTranslucency.ordinal()])
     * 22: /     /     {
     * 23: /     /     case 1:
     * 24: / 107 /       return isWindowShapingSupported();
     *
     * # addAbove(19, 21, 105)
     * javaSrcLines[105] = [21] is already set when this method is called. This method creates the
     * following entries in javaSrcLines:
     * javaSrcLines[103] = [19]
     * javaSrcLines[104] = [20]
     *
     * # addAbove(19, 24, 107)
     * javaSrcLines[107] = [24] is already set when this method is called. This method creates the
     * following entries in javaSrcLines:
     * javaSrcLines[106] = [23]
     *
     * javaSrcLines[105] already has an entry so we add input line 22 to javaSrcLines[106]:
     * javaSrcLines[105] = [22,23]
     *
     * The result is the following folding of the code:
     *
     * 103: /     /   public static boolean isTranslucencySupported(Translucency paramTranslucency)
     * 104: /     /   {
     * 105: / 105 /     switch (1.$SwitchMap$com$sun$awt$AWTUtilities$Translucency[paramTranslucency.ordinal()])
     * 106: /     /     {/     /     case 1:
     * 107: / 107 /       return isWindowShapingSupported();
     */

    // Iterate backwards through the input lines towards inputBeginLineNo
    while(inputBeginLineNo <= (inputLineNo-offset)){

      int offsetInputLine = inputLineNo-offset;
      InputLine inputLine=inputLines.get(offsetInputLine);

      if(inputLine.outputLineNum==-1){
        // Got an InputLine without a corresponding Java source line
        JavaSrcLine javaSrcLine=null;
        int offsetOutputLine = outputLineNo-offset;

        if(offsetOutputLine > 0){
          javaSrcLine=initJavaSrcListItem(offsetOutputLine);
        }

        if(offsetOutputLine==1 || javaSrcLine.inputLines.size()>0){
          // We have reached the start of the file OR the current
          // javaSrcLine has some output lines

          int offsetOutputLineNext = offsetOutputLine + 1;

          // Get the JavaSrcLine for the output line after the current one
          JavaSrcLine javaSrcLineNext=initJavaSrcListItem(offsetOutputLineNext);

          // Iterate backwards through the input lines towards inputBeginLineNo from the output offset
          for(int innerOffset=offset; (inputLineNo-innerOffset)>=inputBeginLineNo; innerOffset++){

            int innerOffsetInputLine = inputLineNo-innerOffset;
            inputLine=inputLines.get(innerOffsetInputLine);
            if(inputLine.outputLineNum==-1){
              // Found an input line without a source line number - add it to javaSrcLineNext
              javaSrcLineNext.inputLines.add(0,innerOffsetInputLine);
              inputLine.calculatedNumLineJavaSrc=offsetOutputLineNext;
            }else
            {
              // Got an InputLine with a corresponding Java source line
              // we must already have handled this line and the ones above
              // it. Time to bail out!
              break;
            }
          }
          // Run out of lines to process - bail out
          break;
        }

        // Add the offsetInputLine to the current javaSrcLine
        javaSrcLine.inputLines.add(offsetInputLine);
        inputLine.calculatedNumLineJavaSrc=offsetOutputLine;
      }else
      {
        // Got an InputLine with a corresponding Java source line
        // we must already have handled this line and the ones above
        // it. Time to bail out!
        break;
      }
      offset++;
    }
  }

  private void addBelow(int inputEndLineNo,int inputLineNo,int outputLineNo){

    int offset=1;

    // Iterate forwards through the input lines towards inputEndLineNo
    while((inputLineNo+offset)<=inputEndLineNo){

      int offsetInputLine = inputLineNo+offset;
      InputLine outputLine=inputLines.get(offsetInputLine);

      if(outputLine.outputLineNum==-1){
        // Got an InputLine without a corresponding Java source line
        int offsetOutputLine = outputLineNo+offset;
        JavaSrcLine javaSrcLine=initJavaSrcListItem(offsetOutputLine);

        if(javaSrcLine.inputLines.size()>0){
          // The current javaSrcLine has some output lines
          int offsetOutputLinePrev = offsetOutputLine - 1;

          // Get the JavaSrcLine for the output line after the current one
          JavaSrcLine javaSrcLinePrev=initJavaSrcListItem(offsetOutputLinePrev);

          // Iterate forwards through the input lines towards inputEndLineNo from the output offset
          for(int innerOffset=offset;(inputLineNo+innerOffset)<=inputEndLineNo;innerOffset++){

            int innerOffsetInputLine = inputLineNo+innerOffset;
            outputLine=inputLines.get(innerOffsetInputLine);
            if(outputLine.outputLineNum==-1){
              // Found an input line without a source line number - add it to javaSrcLineNext
              javaSrcLinePrev.inputLines.add(innerOffsetInputLine);
              outputLine.calculatedNumLineJavaSrc=offsetOutputLinePrev;
            }else
            {
              // Got an InputLine with a corresponding Java source line
              // we must already have handled this line and the ones above
              // it. Time to bail out!
              break;
            }
          }
          // Run out of lines to process - bail out
          break;
        }
        javaSrcLine.inputLines.add(offsetInputLine);
        outputLine.calculatedNumLineJavaSrc=offsetOutputLine;
      }else
      {
        // Got an InputLine with a corresponding Java source line
        // we must already have handled this line and the ones above
        // it. Time to bail out!
        break;
      }
      offset++;
    }
  }

  private void processTypes(AbstractTypeDeclaration rootType){
    List<?> declarations=rootType.bodyDeclarations();
    for (Object declaration : declarations) {
      if (declaration instanceof AbstractTypeDeclaration)
      {
        AbstractTypeDeclaration typeDeclaration = (AbstractTypeDeclaration) declaration;
        processTypes(typeDeclaration);
      }
    }

    int beginTypeLine=Integer.MAX_VALUE;
    int endTypeLine=Integer.MIN_VALUE;
    int firstMethodLine=Integer.MAX_VALUE;
    int lastMethodLine=Integer.MIN_VALUE;

    int beginTypeInputLineNo = unit.getLineNumber(rootType.getStartPosition());
    int endTypeInputLineNo   = unit.getLineNumber(rootType.getStartPosition()+rootType.getLength()-1);

    // Iterate forward through the input line numbers of the type
    for(int inputLineNo=beginTypeInputLineNo; inputLineNo<=endTypeInputLineNo; inputLineNo++){

      // Get the output line number
      InputLine inputLine=inputLines.get(inputLineNo);
      int numLineJavaSrc=inputLine.outputLineNum;
      if(numLineJavaSrc==-1)
        numLineJavaSrc=inputLine.calculatedNumLineJavaSrc;

      if(numLineJavaSrc!=-1){
        // Update the type begin/end output line numbers
        if(beginTypeLine>numLineJavaSrc)
          beginTypeLine=numLineJavaSrc;
        if(endTypeLine<numLineJavaSrc)
          endTypeLine=numLineJavaSrc;

        // Update the type first/last method input line numbers
        if(firstMethodLine>inputLineNo)
          firstMethodLine=inputLineNo;
        if(lastMethodLine<inputLineNo)
          lastMethodLine=inputLineNo;
      }
    }

    // Process the lines above and below this type
    if(beginTypeLine!=Integer.MAX_VALUE){
      addAbove(beginTypeInputLineNo,firstMethodLine,beginTypeLine);
      addBelow(endTypeInputLineNo,  lastMethodLine, endTypeLine);
    }
  }

  @SuppressWarnings("unchecked")
  private void processMembers(AbstractTypeDeclaration rootType){

    // Iterate over the declarations in this type
    List<Object> bodyDeclarations = new ArrayList<Object>();
    if (rootType instanceof EnumDeclaration)
    {
      EnumDeclaration enumDeclaration = (EnumDeclaration) rootType;
      List<?> enumDeclarations = enumDeclaration.enumConstants();

      // Iterate over the enum constant declarations
      int lastInputLineNo = -1;
      for (Object enumDeclObj : enumDeclarations)
      {
        if (enumDeclObj instanceof EnumConstantDeclaration)
        {
          ASTNode element = (ASTNode)enumDeclObj;
          int p=element.getStartPosition();
          int inputBeginLine=unit.getLineNumber(p);

          // If this declaration is on a new line add it to the bodyDeclarations
          if (inputBeginLine != lastInputLineNo)
            bodyDeclarations.add(enumDeclObj);

          lastInputLineNo = inputBeginLine;
        }
      }
    }
    bodyDeclarations.addAll(rootType.bodyDeclarations());

    for (Object bodyDeclaration : bodyDeclarations) {
      if ((bodyDeclaration instanceof MethodDeclaration) ||
        (bodyDeclaration instanceof Initializer) ||
        (bodyDeclaration instanceof FieldDeclaration) ||
        (bodyDeclaration instanceof EnumConstantDeclaration))
      {
        ASTNode element = (ASTNode) bodyDeclaration;
        int p=element.getStartPosition();
        int inputBeginLine=unit.getLineNumber(p);
        int inputEndLine=unit.getLineNumber(p+element.getLength()-1);
        processMember(inputBeginLine, inputEndLine);
      }
    }
  }

  private void processMember(int inputBeginLine, int inputEndLine) {
    int lastOutputLine=-1;
    int lastInputLine=-1;

    // Iterate over the lines in this member
    for(int inputNumLine=inputBeginLine;inputNumLine<=inputEndLine;inputNumLine++){

      // Parse the commented line number if available
      InputLine inputLine=inputLines.get(inputNumLine);
      inputLine.outputLineNum=parseJavaLineNumber(inputLine.line);

      if(inputLine.outputLineNum>1){

        // We have a commented line number!
        lastOutputLine=inputLine.outputLineNum;
        lastInputLine=inputNumLine;

        // Add the input line to the output JavaSrcLine
        JavaSrcLine javaSrcLine=initJavaSrcListItem(inputLine.outputLineNum);
        javaSrcLine.inputLines.add(inputNumLine);
        addAbove(inputBeginLine,inputNumLine,inputLine.outputLineNum);
      }
    }

    if(lastInputLine!=-1 && lastInputLine<inputEndLine)
      addBelow(inputEndLine,lastInputLine,lastOutputLine);
  }

  private void processElements(AbstractTypeDeclaration rootType){
    if ((rootType instanceof TypeDeclaration) ||
        (rootType instanceof EnumDeclaration))
    {
      processMembers(rootType);
    }

    // Recurse into inner types and process their methods
    List<?> bodyDeclarations=rootType.bodyDeclarations();
    for (Object bodyDeclaration : bodyDeclarations) {
      if (bodyDeclaration instanceof AbstractTypeDeclaration)
      {
        processElements((AbstractTypeDeclaration)bodyDeclaration);
      }
    }
  }
}
TOP

Related Classes of jd.ide.eclipse.realignment.editors.DecompilerOutputUtil$InputLine

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.