All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdesktop.swingx.demos.svg.SvgTranscoderDemo Maven / Gradle / Ivy

package org.jdesktop.swingx.demos.svg;

import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridLayout;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import javax.annotation.processing.Processor;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import org.jdesktop.swingx.JXFrame;
import org.jdesktop.swingx.JXFrame.StartPosition;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.icon.SizingConstants;
import org.jdesktop.swingx.icon.ArrowIcon;
import org.jdesktop.swingx.icon.RadianceIcon;
import org.pushingpixels.radiance.tools.svgtranscoder.api.LanguageRenderer;
import org.pushingpixels.radiance.tools.svgtranscoder.api.SvgTranscoder;
import org.pushingpixels.radiance.tools.svgtranscoder.api.java.JavaLanguageRenderer;

import swingset.AbstractDemo;

public class SvgTranscoderDemo extends AbstractDemo {

	private static final long serialVersionUID = 5491188431493759568L;
	private static final Logger LOG = Logger.getLogger(SvgTranscoderDemo.class.getName());
	private static final String DESCRIPTION = "Demonstrates ... .";

    /**
     * main method allows us to run as a standalone demo.
     */
    public static void main(String[] args) {
    	SwingUtilities.invokeLater(new Runnable() {
    		static final boolean exitOnClose = true;
			@Override
			public void run() {
				JXFrame controller = new JXFrame("controller", exitOnClose);
				AbstractDemo demo = new SvgTranscoderDemo(controller);
				JXFrame frame = new JXFrame(DESCRIPTION, exitOnClose);
				frame.setStartPosition(StartPosition.CenterInScreen);
            	frame.getContentPane().add(demo);
            	frame.pack();
            	frame.setVisible(true);
				
				controller.getContentPane().add(demo.getControlPane());
				controller.pack();
				controller.setVisible(true);
			}		
    	});
    }

    private JTextArea textArea;

    /**
     * Constructor
     * @param frame controller Frame frame.title will be set
     */
    public SvgTranscoderDemo(Frame frame) {
    	super(new BorderLayout());
    	frame.setTitle(getBundleString("frame.title", DESCRIPTION));
    	super.setPreferredSize(PREFERRED_SIZE);
    	super.setBorder(new BevelBorder(BevelBorder.LOWERED));

    	textArea = new JTextArea();
		textArea.setColumns(20);
		textArea.setLineWrap(true);
		textArea.setRows(5);
		textArea.setWrapStyleWord(true);
		textArea.setEditable(false);
//		jScrollPane1 = new JScrollPane(textArea);
    	add(textArea);
    	
//    	JPanel buttonPanel = new JPanel(new BoxLayout());
        JPanel panel = new JXPanel(new GridLayout(0, 6, 1, 1)); // zero meaning any number of rows
        add(panel, BorderLayout.NORTH);
        RadianceIcon duke = Duke.factory().createNewIcon();
        duke.setDimension(new Dimension(SizingConstants.BUTTON_ICON, SizingConstants.BUTTON_ICON));
    	panel.add(new JButton("Duke", duke));
    	Icon activity = //new IconTactivity();
    			FeatheRactivity.factory().createNewIcon();
    	JButton activityButton = new JButton("activity", activity);
    	panel.add(activityButton);
    	panel.add(new JButton("airplay", FeatheRairplay.factory().createNewIcon()));
    	panel.add(new JButton("alert_octagon", FeatheRalert_octagon.factory().createNewIcon()));
    	panel.add(new JButton("alert_triangle", FeatheRalert_triangle.factory().createNewIcon()));
    	panel.add(new JButton("align_center", FeatheRalign_center.factory().createNewIcon()));
    	panel.add(new JButton("align_justify", FeatheRalign_justify.factory().createNewIcon()));
    	panel.add(new JButton("align_left", FeatheRalign_left.factory().createNewIcon()));
    	panel.add(new JButton("anchor", FeatheRanchor.factory().createNewIcon()));
    	panel.add(new JButton("aperture", FeatheRaperture.factory().createNewIcon()));
    	panel.add(new JButton("arrow N", ArrowIcon.of(SizingConstants.ACTION_ICON, SizingConstants.ACTION_ICON)));
    	RadianceIcon ne = ArrowIcon.of(RadianceIcon.ACTION_ICON, RadianceIcon.ACTION_ICON);
    	ne.setRotation(RadianceIcon.NORTH_EAST);
    	panel.add(new JButton("arrow NE", ne));

        RadianceIcon duke_waving = Duke_waving.factory().createNewIcon();
        duke_waving.setDimension(new Dimension(SizingConstants.LAUNCHER_ICON, SizingConstants.LAUNCHER_ICON));
    	add(new JButton("Duke_waving", duke_waving), BorderLayout.SOUTH);

        InputStream in = getClass().getResourceAsStream("resources/Duke.svg");
        try {
        	LOG.info("read content.txt");
            textArea.read(new InputStreamReader(in), null);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        /**
         * Creates a new transcoder.
         *
         * @param uri              URI of the SVG image.
         * @param classname        Classname for the generated Java2D code.
         * @param languageRenderer Language renderer for the generated Java2D code.
         */
        LanguageRenderer lr = new JavaLanguageRenderer();
    	LOG.info("LanguageRenderer:"+lr);
        SvgTranscoder st = new SvgTranscoder("resources/activity.svg", "SVGIcon", lr);
//        InputStream templateStream
        st.transcode(in);
        
        String svgName = "at_sign"; // ohne fileType
        String baseName = "FeatheR"+svgName;
        String className = "org.jdesktop.swingx.demos.svg."+baseName;
        Class test = dynCompile(getClass().getResourceAsStream("resources/"+baseName+".java"), className);
    	LOG.info("Hurra und jetzt ...");
		try {
			Constructor ctor = test.getDeclaredConstructor();
			ctor.setAccessible(true); // ist private
			Object o = ctor.newInstance();
	    	panel.add(new JButton(svgName, (RadianceIcon)o));
		} catch (IllegalArgumentException | SecurityException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
//        try {
//			Object instance = test.getDeclaredConstructor().newInstance();
//			
//			// geht auch so:
//        	Supplier interfaceType = (Supplier)test.getDeclaredConstructor().newInstance();
//        	LOG.info("Hurra");
//			
//			if(instance instanceof Supplier s) {
//				LOG.info("Hurra und jetzt ...");
//				LOG.info("get() ..."+s.get()); // funktioniert mit Hello World!
//			}
//		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
//				| NoSuchMethodException | SecurityException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}

    }

/*

// Compiling source 
File root = new File("scripts");
File sourceFile = new File(root, "Test.java");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());

 */
    private Class dynCompile(InputStream inSrc, String className) {
    	JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    	// https://dzone.com/articles/how-to-compile-a-class-at-runtime-with-java-8-and
    	if (compiler == null)
            throw new RuntimeException("No compiler was provided by ToolProvider.getSystemJavaCompiler()." +
            		"Make sure the jdk.compiler module is available.");
    	ClassFileManager fileManager = new ClassFileManager(
    		compiler.getStandardFileManager(null, null, null)
    	);
    	Lookup lookup = MethodHandles.lookup();
    	ClassLoader cl = lookup.lookupClass().getClassLoader();
    	List files = new ArrayList<>();
//    	String className = "com.example.CompileTest";
//    	String content = "package com.example;\n" +
//    			"public class CompileTest implements java.util.function.Supplier {\n" +
//    			"  public String get() {\n" +
//    			"    return \"Hello World!\";\n" +
//    			"  }\n" +
//    			"}\n";
    	LOG.info("compile className "+className);
    	String content = new BufferedReader(
    		      new InputStreamReader(inSrc, StandardCharsets.UTF_8))
    		        .lines()
    		        .collect(Collectors.joining("\n"));
    	LOG.info("content : "+content);
    	files.add(new CharSequenceJavaFileObject(className, content));
    	StringWriter out = new StringWriter();
    	CompileOptions compileOptions = new CompileOptions();
        List options = new ArrayList<>(compileOptions.options);
        if (!options.contains("-classpath")) {
            StringBuilder classpath = new StringBuilder();
            String separator = System.getProperty("path.separator");
            String cp = System.getProperty("java.class.path");
            String mp = System.getProperty("jdk.module.path");

            if (cp != null && !"".equals(cp))
                classpath.append(cp);
            if (mp != null && !"".equals(mp))
                classpath.append(mp);

            if (cl instanceof URLClassLoader) {
                for (URL url : ((URLClassLoader) cl).getURLs()) {
                    if (classpath.length() > 0)
                        classpath.append(separator);

                    if ("file".equals(url.getProtocol()))
						try {
							classpath.append(new File(url.toURI()));
						} catch (URISyntaxException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
                }
            }

            options.addAll(Arrays.asList("-classpath", classpath.toString()));
        }

        CompilationTask task = compiler.getTask(out, fileManager, null, options, null, files);
        if (!compileOptions.processors.isEmpty())
            task.setProcessors(compileOptions.processors);
        task.call();
        boolean expectResult = true;
        if (fileManager.isEmpty()) {
            if (!expectResult) {
            	LOG.info("expectResult == false ==>return null");
            	return null;
            }
            throw new RuntimeException("Compilation error: " + out);
        }
        LOG.info("Compilation OK!!!! fileManager:"+fileManager.toString());
        Class result = null;
        // This works if we have private-access to the interfaces in the class hierarchy
//        if (Reflect.CACHED_LOOKUP_CONSTRUCTOR != null) {
//            result = fileManager.loadAndReturnMainClass(className,
//                (name, bytes) -> Reflect.on(cl).call("defineClass", name, bytes, 0, bytes.length).get());
//        } else {
        /* [java-11] */

        // Lookup.defineClass() has only been introduced in Java 9. 
        // It is required to get private-access to interfaces in the class hierarchy
        
        // This method is called by client code from two levels up the current stack frame
        // We need a private-access lookup from the class in that stack frame in order to get
        // private-access to any local interfaces at that location.
        Class caller = StackWalker
            .getInstance(RETAIN_CLASS_REFERENCE)
            .walk(s -> s
                .skip(2)
                .findFirst()
                .get()
                .getDeclaringClass());
        // If the compiled class is in the same package as the caller class, 
        // then we can use the private-access Lookup of the caller class
        if (className.startsWith(caller.getPackageName() + ".") &&

            // [#74] This heuristic is necessary to prevent classes in subpackages of the caller to be loaded
            //       this way, as subpackages cannot access private content in super packages.
            //       The heuristic will work only with classes that follow standard naming conventions.
            //       A better implementation is difficult at this point.
            Character.isUpperCase(className.charAt(caller.getPackageName().length() + 1))) {
        	LOG.info("compiled class is in the same package as the caller class");
			try {
				Lookup privateLookup = MethodHandles.privateLookupIn(caller, lookup);
				result = fileManager.loadAndReturnMainClass(className,
						(name, bytes) -> privateLookup.defineClass(bytes));
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }

        // Otherwise, use an arbitrary class loader. This approach doesn't allow for
        // loading private-access interfaces in the compiled class's type hierarchy
        else {
        	LOG.info("compiled class is in different package as the caller class");
            ByteArrayClassLoader c = new ByteArrayClassLoader(fileManager.classes());
			try {
	            result = fileManager.loadAndReturnMainClass(className,
	                    (name, bytes) -> c.loadClass(name));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }
        return result;
    	
//    	// run(InputStream in, OutputStream out, OutputStream err, String... arguments)
//    	int rc = compiler.run(inSrc, null, null, null);
    }
// aus https://github.com/jOOQ/jOOR
    static final class ByteArrayClassLoader extends ClassLoader {
        private final Map classes;

        ByteArrayClassLoader(Map classes) {
            super(ByteArrayClassLoader.class.getClassLoader());

            this.classes = classes;
        }

        @Override
        protected Class findClass(String name) throws ClassNotFoundException {
            byte[] bytes = classes.get(name);

            if (bytes == null)
                return super.findClass(name);
            else
                return defineClass(name, bytes, 0, bytes.length);
        }
    }

    static final class JavaFileObject extends SimpleJavaFileObject {
        final ByteArrayOutputStream os = new ByteArrayOutputStream();

        JavaFileObject(String name, JavaFileObject.Kind kind) {
            super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
        }

        byte[] getBytes() {
            return os.toByteArray();
        }

        @Override
        public OutputStream openOutputStream() {
            return os;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return new String(os.toByteArray(), StandardCharsets.UTF_8);
        }
    }

    @FunctionalInterface
    interface ThrowingBiFunction {
        R apply(T t, U u) throws Exception;
    }

    static final class CharSequenceJavaFileObject extends SimpleJavaFileObject {
        final CharSequence content;

        public CharSequenceJavaFileObject(String className, CharSequence content) {
            super(URI.create("string:///" + className.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension), JavaFileObject.Kind.SOURCE);
            this.content = content;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return content;
        }
    }

    static final class ClassFileManager extends ForwardingJavaFileManager {
        private final Map fileObjectMap;
        private Map classes;

        ClassFileManager(StandardJavaFileManager standardManager) {
            super(standardManager);

            fileObjectMap = new LinkedHashMap<>();
        }

        @Override
        public JavaFileObject getJavaFileForOutput(
            JavaFileManager.Location location,
            String className,
            JavaFileObject.Kind kind,
            FileObject sibling
        ) {
            JavaFileObject result = new JavaFileObject(className, kind);
            fileObjectMap.put(className, result);
            return result;
        }

        boolean isEmpty() {
            return fileObjectMap.isEmpty();
        }

        Map classes() {
            if (classes == null) {
                classes = new LinkedHashMap<>();

                for (Entry entry : fileObjectMap.entrySet())
                    classes.put(entry.getKey(), entry.getValue().getBytes());
            }

            return classes;
        }

        Class loadAndReturnMainClass(String mainClassName, ThrowingBiFunction> definer) throws Exception {
            Class result = null;

            // [#117] We don't know the subclass hierarchy of the top level
            //        classes in the compilation unit, and we can't find out
            //        without either:
            //
            //        - class loading them (which fails due to NoClassDefFoundError)
            //        - using a library like ASM (which is a big and painful dependency)
            //
            //        Simple workaround: try until it works, in O(n^2), where n
            //        can be reasonably expected to be small.
            Deque> queue = new ArrayDeque<>(classes().entrySet());
            int n1 = queue.size();

            // Try at most n times
            for (int i1 = 0; i1 < n1 && !queue.isEmpty(); i1++) {
                int n2 = queue.size();

                for (int i2 = 0; i2 < n2; i2++) {
                    Entry entry = queue.pop();

                    try {
                        Class c = definer.apply(entry.getKey(), entry.getValue());

                        if (mainClassName.equals(entry.getKey()))
                            result = c;
                    }
                    // public class ReflectException extends RuntimeException {
                    catch (RuntimeException e) {
                        queue.offer(entry);
                    }
                }
            }

            return result;
        }
    }
    public final class CompileOptions {

        final List processors;
        final List              options;

        public CompileOptions() {
            this(
                Collections.emptyList(),
                Collections.emptyList()
            );
        }

        private CompileOptions(
            List processors,
            List options
        ) {
            this.processors = processors;
            this.options = options;
        }

        public final CompileOptions processors(Processor... newProcessors) {
            return processors(Arrays.asList(newProcessors));
        }

        public final CompileOptions processors(List newProcessors) {
            return new CompileOptions(newProcessors, options);
        }

        public final CompileOptions options(String... newOptions) {
            return options(Arrays.asList(newOptions));
        }

        public final CompileOptions options(List newOptions) {
            return new CompileOptions(processors, newOptions);
        }

        final boolean hasOption(String opt) {
            for (String option : options)
                if (option.equalsIgnoreCase(opt))
                    return true;

            return false;
        }
    }

    @Override
	public JXPanel getControlPane() {
		return emptyControlPane();
	}


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy