/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.print.*;
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

public class ClassDiagrammer {
    JFrame     frame;
    JTextField classNameText;
    JCheckBox  includePkgCheck;
    MyCanvas   canvas;
    UMLClass   umlClass = null;


    public static void main(String[] args) {
        if (args.length > 0) {
            new ClassDiagrammer(args[0]);
            return;
        }
        new ClassDiagrammer();
    }

    public ClassDiagrammer(String fileName) {
        classNameText = new JTextField("");
        includePkgCheck = new JCheckBox("package", false);

        try {
            BufferedReader br = new BufferedReader(new FileReader(fileName));
    
            File jpegFile; 
            String className;
            while((className = br.readLine()) != null) {
                classNameText.setText(className);
                getNewUMLClass();
                if (umlClass != null) {
                    jpegFile = new File(umlClass.getSuggestedFileName());
                    System.out.println("writing " + jpegFile);
                    writeFile(jpegFile);
                }
            }
        } catch (IOException e) {
            e.printStackTrace(); // XXX -- ought to do better than this
        }
        System.exit(0);
    }

    public ClassDiagrammer() {
        frame = new JFrame("Class Diagrammer");
        canvas = new MyCanvas();
        canvas.setBackground(Color.white);

        JPanel input = new JPanel(new FlowLayout());
        classNameText = new JTextField("java.awt.Dimension", 30);
        input.add(classNameText);
        JButton printButton = new JButton("Print...");
        input.add(printButton);
        JButton htmlButton = new JButton("HTML");
        input.add(htmlButton);
        JButton saveButton = new JButton("Save As...");
        input.add(saveButton);
        includePkgCheck = new JCheckBox("package");
        input.add(includePkgCheck);

        frame.getContentPane().add(input, BorderLayout.NORTH);
        frame.getContentPane().add(canvas);

        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent event) {
                System.exit(0);
            } 
        });

        classNameText.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                getNewUMLClass();
                canvas.repaint();
            }
        });

        includePkgCheck.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                getNewUMLClass();
                canvas.repaint();
            }
        });

        printButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                print();
            }
        });

        htmlButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                printHtml();
            }
        });

        saveButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                saveAs();
            }
        });

        getNewUMLClass();

        frame.setBounds(150, 150, 700, 500);
        frame.setVisible(true);
    }

    private void getNewUMLClass() {
        umlClass = null;
        Class c = null;
        
        try {
            c = Class.forName(classNameText.getText());
        } catch (ClassNotFoundException e) {
            // do nothing
        }

        if (c != null) {
            umlClass = new UMLClass(c);
        }
    }
 
    private void saveAs() {
        final JFileChooser chooser = new JFileChooser(System.getProperty("user.dir"));

        chooser.setFileFilter(new FileFilter() {
            public boolean accept(File f) {
                if (f.isDirectory()) {
                    return true;
                } 
                String path = f.getPath().toLowerCase();
                if (path.endsWith(".jpg") || path.endsWith(".jpeg")) {
                    return true;
                } 
                return false;
            } 
            public String getDescription() {
                return "JPEG file (*.jpg,*.jpeg)";
            } 
        });

        chooser.setSelectedFile(new File(umlClass.getSuggestedFileName()));

        if(chooser.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) {
            File f = chooser.getSelectedFile();
            writeFile(f);
        }
    }

    private void writeFile(File f) {
        if (umlClass == null) {
            System.out.println("No class");
        }

        if (f.toString().endsWith(".html")) {
            writeHTML(f);
            return;
        }

        BufferedImage image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2D = image.createGraphics();

        int width  = umlClass.getWidth(g2D) + 1;
        int height = umlClass.getHeight(g2D) + 1;

        g2D.dispose();

        image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        g2D = image.createGraphics();
        g2D.setBackground(Color.white);
        g2D.clearRect(0, 0, width, height);
        g2D.setColor(Color.black);

        umlClass.draw(g2D, 0, 0);

        g2D.dispose();

        try {
            OutputStream os = new FileOutputStream(f);
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);

            encoder.encode(image);
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }

    private void writeHTML(File f) {
        if (umlClass == null) {
            System.out.println("No class");
        }

        try {
            PrintStream writer = new PrintStream(new FileOutputStream(f));

            umlClass.html(writer);

            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }

    private void printHtml() {
        if (umlClass == null) {
            System.out.println("No class");
        }

        umlClass.html(System.out);
    }

    private void print() {
        PrinterJob job = PrinterJob.getPrinterJob();
        PageFormat format = job.defaultPage();
        try {
            job.setPrintable(canvas);
            if (job.printDialog()) {
                job.print();
            }
        } catch (Exception e) {
            e.printStackTrace(); // XXX -- ought to do better than this
        }
        
    }

    public class MyCanvas extends Canvas implements Printable {
        public void paint(Graphics g) {
            Graphics2D g2D = (Graphics2D) g;
            
            if (umlClass != null) {
                umlClass.draw(g2D, 75, 75);
            }
        } 
        public int print(Graphics g, PageFormat pf, int page) throws PrinterException {
            if (page > 0) {
                return NO_SUCH_PAGE;
            }
            Graphics2D g2D = (Graphics2D) g;

            g2D.scale(0.8, 0.8);
            g.setClip((int) pf.getImageableX(), (int) pf.getImageableY(), (int) pf.getImageableWidth(), (int) pf.getImageableHeight());
            paint(g);
            return PAGE_EXISTS;
        }
    }

    public class UMLClass {
        static final int MARGIN = 6;
        String suggestedFileName;
        String[] name;
        String[] fields;
        String[] ctors;
        String[] methods;

        public UMLClass(Class cl) {
            suggestedFileName = stripPackage(cl.getName()) + ".jpg";

            name    = getName(cl);
            fields  = getFields(cl);
            ctors   = getConstructors(cl);
            methods = getMethods(cl);
        }

        String[] getName(Class cl) {
            if (cl.isInterface()) {
                return new String[] {"\u00AB Interface \u00BB", stripPackage(cl.getName())};
            } else {
                return new String[] {stripPackage(cl.getName())};
            }
        }

        String[] getFields(Class cl) {
            Field[]  fields  = cl.getDeclaredFields();
            Vector fieldStrs = new Vector();

            for (int i = 0; i < fields.length; i++) {
                int modifiers = fields[i].getModifiers();
                if (include(modifiers)) {
                    String fieldStr = "";

                    String fieldName = fields[i].getName();

                    // skip compiler-generated references
                    if (fieldName.indexOf("$") == -1) {
                        fieldStr = modifierSymbol(modifiers)
                                 + fieldName
                                 + " : "
                                 + stripPackage(fields[i].getType().getName());
    
                        fieldStrs.add(fieldStr);
                    }
                }
            }

            String[] strings = (String[]) fieldStrs.toArray(new String[0]);
            Arrays.sort(strings);
            return strings;
        }

        String[] getConstructors(Class cl) {
            Constructor[] constrs = cl.getDeclaredConstructors();
            Vector constrStrs = new Vector();

            for (int i = 0; i < constrs.length; i++) {
                int modifiers = constrs[i].getModifiers();
                if (include(modifiers)) {
                    String constrStr = "";

                    constrStr = modifierSymbol(modifiers)
                              + stripPackage(constrs[i].getName())
                              + parameterString(constrs[i].getParameterTypes());
                    constrStrs.add(constrStr);
                }
            }

            String[] strings = (String[]) constrStrs.toArray(new String[0]);
            Arrays.sort(strings);
            return strings;
        }

        String[] getMethods(Class cl) {
            Method[] methods = cl.getDeclaredMethods();
            Vector methodStrs = new Vector();

            for (int i = 0; i < methods.length; i++) {
                int modifiers = methods[i].getModifiers();
                if (include(modifiers)) {
                    String methodStr = "";

                    String methodName = methods[i].getName();
                    
                    // skip compiler-generated accessor methods
                    if (methodName.indexOf("$") == -1) {
                        methodStr = modifierSymbol(modifiers)
                                  + methodName
                                  + parameterString(methods[i].getParameterTypes())
                                  + returnTypeString(methods[i]);
                        methodStrs.add(methodStr);
                    }
                }
            }

            String[] strings = (String[]) methodStrs.toArray(new String[0]);
            Arrays.sort(strings);
            return strings;
        }

        boolean include(int modifiers) {
            if (includePkgCheck.isSelected()) {
                return !Modifier.isPrivate(modifiers);
            } else {
                return Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers);
            }
        }

        public String getSuggestedFileName() {
            return suggestedFileName;
        }

        private String stripPackage(String typeName) {
            String name;
            int pos = typeName.lastIndexOf(".");
            if (pos > -1) {
                name = typeName.substring(pos + 1);
            } else {
                name = typeName;
            }
            if (name.endsWith(";")) {
                name = name.substring(0, name.length() - 1);
            }
            return name;
        }

        private String modifierSymbol(int modifiers) {
            if (Modifier.isPublic(modifiers)) {
                return "+ ";
            } else if (Modifier.isProtected(modifiers)){
                return "# ";
            } else if (Modifier.isPrivate(modifiers)){
                return "- ";
            } else {
                return "  ";
            }
        }

        private String typeAsString(Class type) {
            String name = type.getName();

            int arrayDims = 0;
            while (name.charAt(arrayDims) == '[') {
                arrayDims++;
            }
            String brackets = "";
            if (arrayDims > 0) {
                name = type.getComponentType().getName();
            }
            for (int d = 0; d < arrayDims; d++) {
                brackets += "[]";
            }

            return stripPackage(name) + brackets;
        }

        private String parameterString(Class[] parms) {
            String parmStr = "(";
            for (int i = 0; i < parms.length; i++) {
                if (i > 0) {
                    parmStr += ", ";
                }

                parmStr += typeAsString(parms[i]);
            }
            parmStr += ")";
            return parmStr;
        }

        private String returnTypeString(Method method) {
            String retType = method.getReturnType().getName();


            if (retType.equals("void")) {
                retType = "";
            } else {
                retType = " : " + typeAsString(method.getReturnType());
            }
            return retType;
        }

        public void draw(Graphics2D g, int top, int left) {
            int width  = getWidth(g);
            int height = getHeight(g);

            g.draw(new Rectangle(left, top, width, height));

            FontMetrics fm = g.getFontMetrics();
            int lineHt = fm.getHeight();
            int ascent = fm.getAscent();
            int underLn = -( fm.getAscent() + fm.getLeading());
            int x = left + MARGIN;
            int y = top  + MARGIN + fm.getAscent();

            //name
            for (int i = 0; i < name.length; i++) {
                int strWd = fm.stringWidth(name[i]);
                g.drawString(name[i], left + (width - strWd) / 2, y);
                y += lineHt;
            }
            // line
            y += MARGIN / 2;
            g.drawLine(left, y + underLn, left + width, y + underLn);
            y += MARGIN / 2;
            // properties
            for (int i = 0; i < fields.length; i++) {
                g.drawString(fields[i], x, y);
                y += lineHt;
            }
            // line
            y += MARGIN / 2;
            g.drawLine(left, y + underLn, left + width, y + underLn);
            y += MARGIN / 2;
            // constructors
            for (int i = 0; i < ctors.length; i++) {
                g.drawString(ctors[i], x, y);
                y += lineHt;
            }
            // operations
            for (int i = 0; i < methods.length; i++) {
                g.drawString(methods[i], x, y);
                y += lineHt;
            }

        }

        public int getWidth(Graphics2D g) {
            FontMetrics fm = g.getFontMetrics();
            int width = 0;
            int strWd;

            for (int i = 0; i < name.length; i++) {
                strWd = fm.stringWidth(name[i]);
                if (width < strWd) {
                    width = strWd;
                }
            }
            for (int i = 0; i < fields.length; i++) {
                strWd = fm.stringWidth(fields[i]);
                if (width < strWd) {
                    width = strWd;
                }
            }
            for (int i = 0; i < ctors.length; i++) {
                strWd = fm.stringWidth(ctors[i]);
                if (width < strWd) {
                    width = strWd;
                }
            }
            for (int i = 0; i < methods.length; i++) {
                strWd = fm.stringWidth(methods[i]);
                if (width < strWd) {
                    width = strWd;
                }
            }
            return width + 2 * MARGIN;
        }

        public int getHeight(Graphics2D g) {
            FontMetrics fm = g.getFontMetrics();

            return fm.getHeight() * (name.length + fields.length + ctors.length + methods.length) + 4 * MARGIN;
        }

        public void html(PrintStream writer) {
            writer.println();
            writer.print("<!-- ********** ");
            for (int i = 0; i < name.length; i++) {
                writer.print(name[i] + " ");
            }
            writer.println(" ********** -->");
            writer.print("<table class=\"umlclass\">");

            //name
            writer.println("<tr><td class=\"name\">");
            for (int i = 0; i < name.length; i++) {
                writer.println("    " + name[i] + "<br>");
            }
            writer.print("</td></tr>");

            // properties
            if (fields.length > 0) {
                writer.println("<tr><td class=\"members\" nowrap>");
                for (int i = 0; i < fields.length; i++) {
                    writer.println("    " + fields[i] + "<br>");
                }
                writer.print("</td></tr>");
            }

            // operations
            if (ctors.length + methods.length > 0) {
                // constructors
                writer.println("<tr><td class=\"members\" nowrap>");
                for (int i = 0; i < ctors.length; i++) {
                    writer.println("    " + ctors[i] + "<br>");
                }
                // methods
                for (int i = 0; i < methods.length; i++) {
                    writer.println("    " + methods[i] + "<br>");
                }
                writer.print("</td></tr>");
            }

            writer.println("</table>");
            writer.print("<!-- ********** ");
            for (int i = 0; i < name.length; i++) {
                writer.print(name[i] + " ");
            }
            writer.println(" ********** -->");

            writer.println();
        }
    }

}



/*--- formatting done in "Java Code Conventions" style on 06-28-2000 ---*/


