org.friz.bytecode.Container.java Source code

Java tutorial

Introduction

Here is the source code for org.friz.bytecode.Container.java

Source

/**
 * Copyright (c) 2015 Kyle Friz
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.friz.bytecode;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import org.friz.bytecode.element.ClassElement;
import org.friz.bytecode.refactor.ClassMapping;
import org.friz.bytecode.refactor.RefactorMapper;
import org.friz.bytecode.refactor.Refactorer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.io.ByteStreams;

/**
 * @author Kyle Friz
 * @since Apr 4, 2015
 */
public class Container {

    /**
     * The {@link Logger} instance
     */
    private static Logger logger = LoggerFactory.getLogger(Container.class);

    public boolean jar;
    private String main_class;
    private Map<String, ClassElement> elements;
    private Map<String, ByteBuffer> others;

    private Refactorer refactorer;

    public Container(File file) {
        this.elements = new HashMap<>();
        this.others = new HashMap<>();

        this.refactorer = new Refactorer(this);

        try {
            if (file.getPath().endsWith(".jar")) {
                this.jar = true;

                try (JarFile jar = new JarFile(file)) {
                    this.main_class = jar.getManifest().getMainAttributes().getValue("Main-Class");
                    Enumeration<JarEntry> enumeration = jar.entries();
                    while (enumeration.hasMoreElements()) {
                        JarEntry next = enumeration.nextElement();
                        if (next.getName().endsWith(".class")) {
                            ClassReader reader = new ClassReader(jar.getInputStream(next));
                            ClassNode node = new ClassNode();
                            reader.accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
                            ClassElement element = new ClassElement(node);

                            elements.put(element.name(), element);
                        } else if (!next.getName().contains("META-INF")) {
                            others.put(next.getName(),
                                    ByteBuffer.wrap(ByteStreams.toByteArray(jar.getInputStream(next))));
                        }
                    }
                    jar.close();
                }
            } else if (file.getPath().endsWith(".class")) {
                this.jar = false;

                ClassReader reader = new ClassReader(new FileInputStream(file));
                ClassNode node = new ClassNode();
                reader.accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
                ClassElement element = new ClassElement(node);

                elements.put(element.name(), element);
            }
            logger.info("Loaded " + elements.size() + " Class(es).");
        } catch (IOException e) {
            logger.error("Error loading class(es)!", e);
        }
    }

    public void save(File file) {
        try {
            if (jar) {
                Manifest manif = new Manifest();
                Attributes global = manif.getMainAttributes();

                global.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
                if (main_class != null) {
                    if (refactorer().classes().containsKey(main_class.replace(".", "/")))
                        main_class = refactorer.classes().get(main_class.replace(".", "/")).getRefName();

                    global.put(Attributes.Name.MAIN_CLASS, main_class.replace("/", "."));
                }
                global.put(Attributes.Name.CLASS_PATH, ".");
                global.put(new Attributes.Name("Bundled-By"), "Friz Transformer");
                global.put(new Attributes.Name("Bundle-Version"), "1.0.0");

                try (JarOutputStream output = new JarOutputStream(new FileOutputStream(file), manif)) {
                    for (ClassElement element : elements.values()) {
                        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                        output.putNextEntry(new JarEntry(element.name().replaceAll("\\.", "/") + ".class"));
                        element.node().accept(writer);
                        output.write(writer.toByteArray());
                        output.closeEntry();
                    }
                    for (String key : others.keySet()) {
                        output.putNextEntry(new JarEntry(key));
                        output.write(others.get(key).array());
                        output.closeEntry();
                    }
                }
            } else {
                FileOutputStream output = new FileOutputStream(file);
                for (ClassElement element : elements.values()) {
                    ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                    element.node().accept(writer);
                    output.write(writer.toByteArray());
                }
                output.close();
            }
            logger.info("Saved " + elements.size() + " Class(es).");
        } catch (IOException e) {
            logger.error("Error saving class(es)!", e);
        }
    }

    public Collection<ClassElement> elements() {
        return elements.values();
    }

    public Refactorer refactorer() {
        return refactorer;
    }

    public ClassElement[] getHierarchy(ClassElement c) {
        LinkedList<ClassElement> hierarchy = new LinkedList<ClassElement>();

        ClassElement c2 = c;
        while (c2 != null) {
            hierarchy.addFirst(c2);

            if (c2.superName() == null) {
                break;
            }

            c2 = getClass(c2.superName());
        }

        return hierarchy.toArray(new ClassElement[0]);
    }

    public ClassElement getClass(String name) {
        if (name == null) {
            return null;
        }

        ClassElement c = elements.get(name);

        if (c == null) {
            ClassNode n = new ClassNode();
            ClassReader cr;
            try {
                cr = new ClassReader(name);
            } catch (IOException e) {
                return null;
            }
            cr.accept(n, 0);
            c = new ClassElement(n);
        }
        return c;
    }

    public String getName(String name) {
        if (name == null) {
            return null;
        }

        String newName = null;
        if (refactorer().classes().containsKey(name))
            newName = refactorer().classes().get(name).getRefName();
        else {
            ClassElement el = elements.get(name);
            if (el != null)
                newName = el.name();
        }

        if (newName == null) {
            ClassNode n = new ClassNode();
            ClassReader cr;
            try {
                cr = new ClassReader(name);
            } catch (IOException e) {
                return null;
            }
            cr.accept(n, 0);
            newName = n.name;
        }
        return newName;
    }

    public int getLevel(ClassElement c) {
        if (c.isInterface()) {
            return 0;
        }

        return getHierarchy(c).length - 1;
    }

    /**
     * @param name
     * @param element
     */
    public void relocate(String name, ClassElement element) {
        if (elements.containsKey(name))
            elements.remove(name);

        elements.put(element.name(), element);
    }

}