You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by sc...@apache.org on 2015/11/01 15:49:25 UTC

svn commit: r1711785 - /uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/UimaDecompiler.java

Author: schor
Date: Sun Nov  1 14:49:24 2015
New Revision: 1711785

URL: http://svn.apache.org/viewvc?rev=1711785&view=rev
Log:
[UIMA-4518] a decompiler for JCas cover classes

Added:
    uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/UimaDecompiler.java

Added: uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/UimaDecompiler.java
URL: http://svn.apache.org/viewvc/uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/UimaDecompiler.java?rev=1711785&view=auto
==============================================================================
--- uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/UimaDecompiler.java (added)
+++ uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/UimaDecompiler.java Sun Nov  1 14:49:24 2015
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.uima.cas.impl;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import org.apache.uima.util.Misc;
+
+import com.strobel.assembler.metadata.Buffer;
+import com.strobel.assembler.metadata.ITypeLoader;
+import com.strobel.decompiler.Decompiler;
+import com.strobel.decompiler.DecompilerSettings;
+import com.strobel.decompiler.PlainTextOutput;
+
+/**
+ * Decompiler
+ *   - for testing
+ *   - for locating customizations
+ *
+ * Operation:
+ *   Make an instance, optionally setting
+ *     - class loader to use (may pass byte array instead)
+ *     - directory where to write output (may output to string instead)
+ *   
+ *   call decompile
+ *     - argument
+ *         - class name (without .class or .java suffix, fully qualified) or
+ *         - byte array
+ *     - return value is a byte array output stream with UTF-8 encoded value
+ *     
+ *   decompileToFile - writes decompiled output to a xxx.java file in output directory
+ *   
+ * Not thread safe
+ */
+public class UimaDecompiler {
+  
+  private final static byte[] errorMsg;
+  static {
+    byte[] temp = null;
+    try {
+      temp = "!!! ERROR: Failed to load class".getBytes("UTF-8");
+    } catch (UnsupportedEncodingException e) {
+    }
+    errorMsg = temp;
+  }
+
+  
+  private final DecompilerSettings decompilerSettings = DecompilerSettings.javaDefaults();
+
+  private final ClassLoader classLoader;
+  
+  private File outputDirectory = null;
+  
+  public UimaDecompiler() {
+    classLoader = null;
+  }
+  
+  public UimaDecompiler(ClassLoader classLoader, File outputDirectory) {
+    this.classLoader = classLoader;
+    this.outputDirectory = outputDirectory;
+    if (classLoader != null) {
+      setDecompilerSettingsForClassLoader();
+    }
+  }
+  
+  public ByteArrayOutputStream decompile(String className, byte[] byteArray) {
+    setDecompilerSettingsForByteArray(className.replace('.', '/'), byteArray);
+    return decompileCommon(className);
+  }
+  
+  public ByteArrayOutputStream decompile(String className) {
+    setDecompilerSettingsForClassLoader();
+    return decompileCommon(className);
+  }
+  
+  public ByteArrayOutputStream decompileCommon(String className) {
+    
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    PlainTextOutput plainTextOutput = null;
+    BufferedWriter writer = null;
+    try {
+      plainTextOutput = 
+          new PlainTextOutput(
+            writer = new BufferedWriter(
+              new OutputStreamWriter(baos, "UTF-8")));
+      Decompiler.decompile(className.replace('.', '/'), plainTextOutput, decompilerSettings);
+      writer.close();
+    } catch (IOException e) {
+      throw new RuntimeException(); // can't happen
+    }
+    
+    return baos;
+  }
+  
+  public boolean decompileToOutputDirectory(String className) {
+    ByteArrayOutputStream baos = decompile(className);
+    return writeIfOk(baos, className);
+  }
+
+  public boolean decompileToOutputDirectory(String className, byte[] byteArray) {
+    ByteArrayOutputStream baos = decompile(className, byteArray);
+    return writeIfOk(baos, className);
+  }
+
+  private void setDecompilerSettingsForByteArray(String classNameSlashes, byte[] byteArray) {
+    ITypeLoader tl = new ITypeLoader() {
+      
+      @Override
+      public boolean tryLoadType(String internalName, Buffer buffer) {
+        if (classNameSlashes.equals(internalName)) {
+          int length = byteArray.length;
+          buffer.reset(length);
+          System.arraycopy(byteArray, 0, buffer.array(), 0, length);
+          return true;
+        } else {
+          return false;
+        }
+      }      
+    };    
+    decompilerSettings.setTypeLoader(tl);
+  }
+    
+  public boolean writeIfOk(ByteArrayOutputStream baos, String className) {
+    if (!decompiledFailed(baos)) {
+      Misc.toFile(baos, new File(outputDirectory, className));
+      return true;
+    }
+    return false;    
+  }
+  
+  public boolean decompiledFailed(ByteArrayOutputStream baos) {
+    return (baos.size() == errorMsg.length && Arrays.equals(errorMsg, baos.toByteArray())); 
+  }
+  
+  private void setDecompilerSettingsForClassLoader() {
+    ITypeLoader tl = new ITypeLoader() {
+      
+      @Override
+      public boolean tryLoadType(String internalName, Buffer buffer) {
+        
+        // read the class as a resource, and put into temporary byte array output stream
+        // because we need to know the length
+        
+        internalName = internalName.replace('.', '/') + ".class";
+        InputStream stream = classLoader.getResourceAsStream(internalName);
+        if (stream == null) {
+          return false;
+        }
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 16);        
+        byte[] b = new byte[1024 * 16]; 
+        int numberRead;
+        try {
+          while (0 <= (numberRead = stream.read(b))){
+            baos.write(b, 0, numberRead);
+          }
+        } catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+        
+        // Copy result (based on length) into output buffer spot
+        int length = baos.size();
+        b = baos.toByteArray();
+        buffer.reset(length);
+        System.arraycopy(b, 0, buffer.array(), 0, length);
+        
+        return true;
+      }      
+    };    
+    decompilerSettings.setTypeLoader(tl);
+  }
+}