/*-------------------------------------------------------------------------------
Copyright (c)2000 TogetherSoft Corporation. Patents pending. All rights reserved.
-------------------------------------------------------------------------------*/

package com.togethersoft.modules.inserttags;

import com.togethersoft.openapi.ide.IdeContext;
import com.togethersoft.openapi.ide.IdeScript;
import com.togethersoft.openapi.ide.project.IdeProjectManagerAccess;
import com.togethersoft.openapi.ide.message.IdeMessageManagerAccess;
import com.togethersoft.openapi.ide.window.IdeWindowManager;
import com.togethersoft.openapi.ide.window.IdeWindowManagerAccess;
import com.togethersoft.openapi.ide.window.IdeButtonType;
import com.togethersoft.openapi.ide.window.IdeDialogButtonGroup;
import com.togethersoft.openapi.ide.message.IdeMessageType;
import com.togethersoft.openapi.ide.window.IdeDialogType;
import com.togethersoft.openapi.ide.diagram.IdeDiagramManagerAccess;
import com.togethersoft.openapi.ide.diagram.IdeDiagramManager;
import com.togethersoft.openapi.rwi.RwiVisitorAdapter;
import com.togethersoft.openapi.rwi.RwiElement;
import com.togethersoft.openapi.rwi.RwiPackage;
import com.togethersoft.openapi.rwi.RwiNode;
import com.togethersoft.openapi.rwi.RwiMember;
import com.togethersoft.openapi.rwi.RwiProperty;
import com.togethersoft.openapi.rwi.RwiDiagram;
import com.togethersoft.openapi.rwi.RwiShapeType;
import com.togethersoft.openapi.rwi.enum.RwiNodeEnumeration;
import com.togethersoft.openapi.rwi.enum.RwiPackageEnumeration;
import java.util.Vector;
import java.awt.Dimension;
import javax.swing.JList;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;

/**
 * This sample module inserts the specified tag into the selected source
 * elements (packages/classes/interfaces and operations/attributes)
 * without this kind of a tag.
 *
 * If the user selects a class diagram, this module will process the contents
 * (with subpackages) of its containing package.
 *
 * Note: if you select a package icon, the selected element is RwiDiagram
 * (see API docs, RwiDiagram) and the RwiPackage being represented by it can be
 * obtained via RwiDiagram.getContainingPackage() method (this is done in
 * the TagsInserterVisitor.visitDiagram method). That is why if you select
 * a package icon you will get what you want - it will insert tags in all
 * possible elements within the package and its subpackages. But if you select
 * a free space in a diagram the selected element will also be RwiDiagram,
 * so the module will insert tags not only in this diagram's elements, but in
 * the diagram's containing package elements (and elements in its subpackages).
 * This is because it applies the same algorithm to the RwiDiagrams.
 *
 * Pay attention to how we use visitors - it is a very convenient pattern.
 *
 * @version 3.0.0
 * @author TogetherSoft
 */
public class InsertTags implements IdeScript
{
   /**
    * Runs this module.
    *
    * @param context the IdeContext instance containing
    * the selection information at the moment the module was called.
    */
    public void run(IdeContext context)
    {
        // printing information message
        IdeMessageManagerAccess.printMessage
            (IdeMessageType.INFORMATION, "InsertTags module: started");

        // checking if project is opened
        if (IdeProjectManagerAccess.getProjectManager().getActiveProject() == null)
        {
            // no open project
            // show modal window with error description
            IdeMessageManagerAccess.printMessage
                (IdeMessageType.ERROR_MODAL, "No open project");
            // printing information message
            IdeMessageManagerAccess.printMessage
                (IdeMessageType.INFORMATION, "InsertTags module: finished");
            return;
        }

        // getting diagram manager
        IdeDiagramManager diagramManager =
            IdeDiagramManagerAccess.getDiagramManager();

        // checking if there is not active diagram
        if (diagramManager.getActiveDiagram() == null)
        {
            // no open diagram
            IdeMessageManagerAccess.printMessage
                (IdeMessageType.ERROR_MODAL, "No open diagram");
            IdeMessageManagerAccess.printMessage
                (IdeMessageType.INFORMATION, "InsertTags module: finished");
            return;
        }

        // getting the array of selected RwiElements
        RwiElement[] selectedRwiElements = context.getRwiElements();

        // checking if there are selected elements
        if (selectedRwiElements == null || selectedRwiElements.length == 0)
        {
            // no selected elements
            IdeMessageManagerAccess.printMessage
                (IdeMessageType.ERROR_MODAL, "No selection was made.");
            IdeMessageManagerAccess.printMessage
                (IdeMessageType.INFORMATION, "InsertTags module: finished");
            return;
        }

        // header object for dialog window
        JLabel header = new JLabel("Insert or change tags in " +
            selectedRwiElements.length + " selected elements");

        // getting window manager
        windowManager = IdeWindowManagerAccess.getWindowManager();

        // standard Swing components JTextField for tag and tag value editing
        JTextField tagField = new JTextField(DEFAULT_TAG);
        JTextField tagValueField = new JTextField(DEFAULT_TAG_VALUE);

        // show dialog window
        String answer = windowManager.showOptionDialog(
            null, "Enter the tag and its value",
            IdeMessageType.INFORMATION, new Object[]
            { header, tagField, tagValueField },
            IdeButtonType.OK_CANCEL_BUTTONS, null);

        // checking pressed button
        if (!IdeButtonType.OK.equals(answer))
        {
            // not OK button has been pressed
            IdeMessageManagerAccess.printMessage
                (IdeMessageType.INFORMATION, "InsertTags module: finished");
            return;
        }

        // creation new TagsInserter
        TagsInserter tagsVisitor =
            new TagsInserter(tagField.getText(), tagValueField.getText());

        // processing selected elements
        for (int i = 0; i < selectedRwiElements.length; i++)
        {
            // the visitor inserts the tags
            selectedRwiElements[i].accept(tagsVisitor);
        }

        // show insert result
        showResults(tagsVisitor);

        // printing final message
        IdeMessageManagerAccess.printMessage
            (IdeMessageType.INFORMATION, "InsertTags module: finished");
    }

    /**
     * Shows result of tags insert
     *
     * @param visitor TagsInserter object contains info about insert results
     */
    private void showResults(TagsInserter visitor)
    {
        // preparing visaul components
        JScrollPane pane = new JScrollPane(visitor.getResults());
        pane.setPreferredSize(new Dimension(400, 200));

        // show results window
        windowManager.showMessageDialog
            ("Results", IdeDialogType.INFORMATION,
            new Object[]
            {   "Total source elements analyzed: " + visitor.getTotalProcessed(),
                "Total tags inserted: " + visitor.getTagsInserted(), pane
            });
    }

    private final static String DEFAULT_TAG = "author";        // tag name
    private final static String DEFAULT_TAG_VALUE = "Unknown"; // tag value
    private IdeWindowManager windowManager;                    // window manager

    /**
    * This visitor inserts tags in the various elements.
    */
    private class TagsInserter extends RwiVisitorAdapter
    {
        /**
         * Creates class instance with specified parameters.
         *
         * @param tag  string tag name
         * @param tagValue string tag value
         */
        public TagsInserter(String tag, String tagValue)
        {
            myTag = tag;
            myTagValue = tagValue;
            processedNamesVector = new Vector();
        }

        /**
         * Returns a JList object with the processed elements info
         *
         * @return JList object with the processed elements info
         */
        public JList getResults()
        {
            JList processed = new JList(processedNamesVector);
            processed.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            processed.setSelectedIndex(0);
            return processed;
        }

        /**
         * Returns the number of processed elements.
         *
         * @return the number of processed elements
         */
        public int getTotalProcessed() { return myTotalElements; }

        /**
         * Returns the number of inserted tags.
         *
         * @return the number of inserted tags
         */
        public int getTagsInserted() { return myTagsInserted; }

        /**
         * Processes specified package with it's subpackages
         *
         * @param rwiPackage RwiPackage object that will be processed
         * @return null object
         */
        public Object visitPackage(RwiPackage rwiPackage)
        {
            // getting nodes enumeration
            RwiNodeEnumeration rwiNodeEnumeration = rwiPackage.nodes();

            // processing each node...
            while (rwiNodeEnumeration.hasMoreElements())
            {
                // getting current node
                RwiNode nextRwiNode = rwiNodeEnumeration.nextRwiNode();
                // processing node
                visitNode(nextRwiNode);
            }

            // getting subpackages enumeration
            RwiPackageEnumeration subpackages = rwiPackage.subpackages();

            // processing each subpackage...
            while (subpackages.hasMoreElements())
            {
                // getting current subpackage
                RwiPackage nextSubpackage = subpackages.nextRwiPackage();
                // processing subpackage
                visitPackage(nextSubpackage);
            }

            return null;
        }

        /**
         * Processes packages contained in specified class diagram
         *
         * @param diagram RwiDiagram object that will be processed
         * @return null object
         */
        public Object visitDiagram(RwiDiagram diagram)
        {
            // if it's class diagram
            if (RwiShapeType.CLASS_DIAGRAM.equals
                (diagram.getProperty(RwiProperty.SHAPE_TYPE)))
            {
                // processing contained packages
                visitPackage(diagram.getContainingPackage());
            }
            return null;
        }

        /**
         * Processes specified RWI node
         *
         * @param rwiNode RwiNode object that will be processed
         * @return null object
         */
        public Object visitNode(RwiNode rwiNode)
        {
            String comment = "";  // comment

            // if it's class
            if (RwiShapeType.CLASS.equals
                (rwiNode.getProperty(RwiProperty.SHAPE_TYPE)))
            {
                // if it's interface
                if (rwiNode.hasProperty(RwiProperty.INTERFACE))
                {
                    comment = " (interface) ";
                }
                else  // it's class
                {
                    comment = " (class) ";
                }
                addTag(rwiNode, comment); // add tag
            }
            return null;
        }

        /**
         * Processes specified RWI member
         *
         * @param member RwiMember object that will be processed
         * @return null object
         */
        public Object visitMember(RwiMember member)
        {
            String comment = ""; // comment

            // if it's operation
            if (RwiShapeType.OPERATION.equals
                (member.getProperty(RwiProperty.SHAPE_TYPE)))
            {
                comment = " (operation) ";
                addTag(member, comment);
            }
            // if it's attribute
            else if (RwiShapeType.ATTRIBUTE.equals
                        (member.getProperty(RwiProperty.SHAPE_TYPE)))
            {
                comment = " (attribute) ";
                addTag(member, comment);
            }
            return null;
        }

        /**
         * Adds tag in specified element using RwiPropertyMap.addProperty method.
         *
         * @param diagram RwiDiagram object that will be processed
         * @return null object
         * @see RwiPropertyMap.addProperty
         */
        private void addTag(RwiElement element, String comment)
        {
            // getting element full name property
            String name = element.getProperty(RwiProperty.FULL_NAME);

            // if full name is empty
            if (name == null || "".equals(name))
            {
                // getting name property
                name = element.getProperty(RwiProperty.NAME);
            }

            // if it's possible to add specified tag
            if (element.canAddProperty(myTag, myTagValue))
            {
                // adding tag
                element.addProperty(myTag, myTagValue);

                // adding processed element in result vector
                // and inreasing number of inserted tags
                processedNamesVector.addElement
                    ("processed: " + name + comment + " - tag inserted");
                myTagsInserted++;
            }
            else // impossible insert tag
            {
                // adding processed element in result vector
                processedNamesVector.addElement("processed: " + name + comment);
            }
            myTotalElements++; // increasing number of processed elements
        }

        private Vector processedNamesVector;  // vector of processed elements
        private String myTag;                 // keeps the tag name
        private String myTagValue;            // keeps the tag value
        private int myTagsInserted;           // number of inserted tags
        private int myTotalElements;          // number of processed elements
    }
}


