Ptidej-v5.2-master/POM Tests/data/ArgoUML_0.28/src/org/argouml/notation/providers/uml/StateBodyNotationUml.java
// $Id: StateBodyNotationUml.java 16400 2008-12-21 16:27:58Z tfmorris $
// Copyright (c) 2005-2007 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies.  This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason.  IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

package org.argouml.notation.providers.uml;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.StringTokenizer;

import org.argouml.application.events.ArgoEventPump;
import org.argouml.application.events.ArgoEventTypes;
import org.argouml.application.events.ArgoHelpEvent;
import org.argouml.i18n.Translator;
import org.argouml.model.Model;
import org.argouml.notation.NotationSettings;
import org.argouml.notation.providers.StateBodyNotation;

/**
 * UML notation for the body of a state.
 * 
 * @author Michiel
 */
public class StateBodyNotationUml extends StateBodyNotation {

    /**
     * The default language for an expression.
     */
    private static final String LANGUAGE = "Java";
    
    /**
     * The constructor.
     *
     * @param state the state represented by the notation
     */
    public StateBodyNotationUml(Object state) {
        super(state);
    }

    /*
     * @see org.argouml.uml.notation.NotationProvider#parse(java.lang.Object, java.lang.String)
     */
    public void parse(Object modelElement, String text) {
        try {
            parseStateBody(modelElement, text);
        } catch (ParseException pe) {
            String msg = "statusmsg.bar.error.parsing.statebody";
            Object[] args = {
                pe.getLocalizedMessage(),
                Integer.valueOf(pe.getErrorOffset()),
            };
            ArgoEventPump.fireEvent(new ArgoHelpEvent(
                    ArgoEventTypes.HELP_CHANGED, this,
                    Translator.messageFormat(msg, args)));
        }
    }

    /*
     * @see org.argouml.uml.notation.NotationProvider#getParsingHelp()
     */
    public String getParsingHelp() {
        return "parsing.help.fig-statebody";
    }


    @Override
    public String toString(Object modelElement, NotationSettings settings) {
        return toString(modelElement);
    }
    
    /*
     * @see org.argouml.uml.notation.NotationProvider#toString(java.lang.Object, java.util.Map)
     */
    @SuppressWarnings("deprecation")
    @Deprecated
    public String toString(Object modelElement, Map args) {
        return toString(modelElement);
    }

    private String toString(Object modelElement) {
        StringBuffer s = new StringBuffer();

        Object entryAction = Model.getFacade().getEntry(modelElement);
        Object exitAction = Model.getFacade().getExit(modelElement);
        Object doAction = Model.getFacade().getDoActivity(modelElement);
        if (entryAction != null) {
            String entryStr = 
                NotationUtilityUml.generateActionSequence(entryAction);
            s.append("entry /").append(entryStr);
        }
        if (doAction != null) {
            String doStr = NotationUtilityUml.generateActionSequence(doAction);
            if (s.length() > 0) {
                s.append("\n");
            }
            s.append("do /").append(doStr);

        }
        if (exitAction != null) {
            String exitStr = 
                NotationUtilityUml.generateActionSequence(exitAction);
            if (s.length() > 0) {
                s.append("\n");
            }
            s.append("exit /").append(exitStr);
        }
        Collection internaltrans =
            Model.getFacade().getInternalTransitions(modelElement);
        if (internaltrans != null) {
            for (Object trans : internaltrans) {
                if (s.length() > 0) {
                    s.append("\n");
                }
                /* TODO: Is this a good way of handling nested notation? */
                s.append((new TransitionNotationUml(trans)).toString(trans,
                        NotationSettings.getDefaultSettings()));
            }
        }
        return s.toString();
    }

    /**
     * Parse user input for state bodies and assign the individual lines to
     * according actions or transitions. The user input consists of multiple
     * lines like:<pre>
     *   action-label / action-expression
     * </pre> or the format of a regular
     * transition - see parseTransition(). <p>
     *
     * "action-label" stands for one of "entry", "do" and "exit".
     * The words "entry", "do" and "exit" are case-independent.
     *
     * @param st  The State object.
     * @param s   The string to parse.
     * @throws ParseException when there is a syntax problem,
     *         e.g. non-matching brackets () or []
     */
    protected void parseStateBody(Object st, String s) throws ParseException {
        boolean foundEntry = false;
        boolean foundExit = false;
        boolean foundDo = false;

        /* Generate all the existing internal transitions,
         * so that we can compare them as text with the newly entered ones.
         */
        ModelElementInfoList internalsInfo =
            new ModelElementInfoList(
                    Model.getFacade().getInternalTransitions(st));

        StringTokenizer lines = new StringTokenizer(s, "\n\r");
        while (lines.hasMoreTokens()) {
            String line = lines.nextToken().trim();
            /* Now let's check if the new line is already present in
             * the old list of internal transitions; if it is, then
             * mark the old one to be retained (i.e. do not create a new one),
             * if it isn't, continue with parsing:
             */
            if (!internalsInfo.checkRetain(line)) {
                if (line.toLowerCase().startsWith("entry")
                        && line.substring(5).trim().startsWith("/")) {
                    parseStateEntryAction(st, line);
                    foundEntry = true;
                } else if (line.toLowerCase().startsWith("exit")
                        && line.substring(4).trim().startsWith("/")) {
                    parseStateExitAction(st, line);
                    foundExit = true;
                } else if (line.toLowerCase().startsWith("do")
                        && line.substring(2).trim().startsWith("/")) {
                    parseStateDoAction(st, line);
                    foundDo = true;
                } else {
                    Object t =
                        Model.getStateMachinesFactory()
                                .buildInternalTransition(st);
                    if (t == null) {
                        continue;
                    }
                    /* TODO: If the next line trows an exception, then what
                     * do we do with the remainder of the
                     * parsed/to be parsed lines?
                     */
                    /* TODO: Is this a good way of handling nested notation?
                     * The following fails the tests:
                     * new TransitionNotationUml(t).parse(line);
                     */
                    new TransitionNotationUml(t).parseTransition(t, line);
                    /* Add this new one, and mark it to be retained: */
                    internalsInfo.add(t, true);
                }
            }
        }

        if (!foundEntry) {
            delete(Model.getFacade().getEntry(st));
        }
        if (!foundExit) {
            delete(Model.getFacade().getExit(st));
        }
        if (!foundDo) {
            delete(Model.getFacade().getDoActivity(st));
        }

        /* Process the final list of internal transitions,
         * and hook it to the state:
         */
        Model.getStateMachinesHelper().setInternalTransitions(st,
                internalsInfo.finalisedList());
    }

    /**
     * This class manages a list of UML modelelements that existed
     * before and after the parseXxxxx() function was called.
     * It has all the knowledge to deal with additions and removals.
     *
     * @author MVW
     */
    class ModelElementInfoList {
        /**
         * The list that we maintain.
         */
        private Collection<InfoItem> theList;

        /**
         * An item in a list, maintains all info about one UML object,
         * its generated version (i.e. textual representation),
         * and if it needs to be retained after parsing.<p>
         *
         * @author MVW
         */
        class InfoItem {
            private TransitionNotationUml generator;
            private Object umlObject;
            private boolean retainIt;

            /**
             * The constructor.
             * @param obj the UML object
             */
            InfoItem(Object obj) {
                umlObject = obj;
                generator = new TransitionNotationUml(obj);
            }

            /**
             * The constructor.
             *
             * @param obj the UML object.
             * @param r
             */
            InfoItem(Object obj, boolean r) {
                this(obj);
                retainIt = r;
            }

            /**
             * @return the generated string representation
             */
            String getGenerated() {
                return generator.toString();
            }

            /**
             * @return the UML Object
             */
            Object getUmlObject() {
                return umlObject;
            }

            /**
             * Retain this UML object.
             */
            void retain() {
                retainIt = true;
            }

            /**
             * @return true if the UML object is to be retained,
             *         false if it is to be deleted
             */
            boolean isRetained() {
                return retainIt;
            }
        }

        /**
         * The constructor.
         *
         * @param c the collection of the UML objects
         *          that were present before
         */
        ModelElementInfoList(Collection c) {
            theList = new ArrayList<InfoItem>();
            for (Object obj : c) {
                theList.add(new InfoItem(obj));
            }
        }

        /**
         * @param obj the UML object
         * @param r true if this UML object needs to be retained
         */
        void add(Object obj, boolean r) {
            theList.add(new InfoItem(obj, r));
        }

        /**
         * Check the given textual description,
         * and if already present in the list, then retain it.
         * @param line the given textual description
         * @return true if the item was already present in the list
         */
        boolean checkRetain(String line) {
            for (InfoItem tInfo : theList) {
                if (tInfo.getGenerated().equals(line)) {
                    tInfo.retain();
                    return true;
                }
            }
            return false;
        }

        /**
         * Finish the procedure, by deleting the UML model items
         * that are not to be retained, and return a collection
         * of the ones to be retained.
         * This method should only be called once!
         * @return the UML objects that survive.
         */
        Collection finalisedList() {
            // don't forget to remove old internals!
            Collection<Object> newModelElementsList = new ArrayList<Object>();
            for (InfoItem tInfo : theList) {
                if (tInfo.isRetained()) {
                    newModelElementsList.add(tInfo.getUmlObject());
                } else {
                    delete(tInfo.getUmlObject());
                }
            }
            // Make next accesses to this instance predictable:
            theList.clear();
            // and hook in the new ones:
            return newModelElementsList;
        }
    }


    /**
     * Parse a line of the form: "entry /action" and create an action.
     * We do not need to check for the presence of the word "entry" - that
     * is done by the caller.
     *
     * @param st  the state object
     * @param s   the string to be parsed
     */
    private void parseStateEntryAction(Object st, String s) {
        if (s.indexOf("/") > -1) {
            s = s.substring(s.indexOf("/") + 1).trim();
        }
        Object oldEntry = Model.getFacade().getEntry(st);
        if (oldEntry == null) {
            Model.getStateMachinesHelper().setEntry(st, buildNewCallAction(s));
        } else {
            updateAction(oldEntry, s);
        }
    }

    /**
     * Parse a line of the form: "exit /action" and create an action.
     * We do not need to check for the presence of the word "exit" - that
     * is done by the caller.
     *
     * @param st
     *            the state object
     * @param s
     *            the string to be parsed
     */
    private void parseStateExitAction(Object st, String s) {
        if (s.indexOf("/") > -1) {
            s = s.substring(s.indexOf("/") + 1).trim();
        }
        Object oldExit = Model.getFacade().getExit(st);
        if (oldExit == null) {
            Model.getStateMachinesHelper().setExit(st, buildNewCallAction(s));
        } else {
            updateAction(oldExit, s);
        }
    }

    /**
     * Parse a line of the form: "do /action" and create an action.
     * We do not need to check for the presence of the word "do" - that
     * is done by the caller.
     *
     * @param st  the state object
     * @param s   the string to be parsed
     */
    private void parseStateDoAction(Object st, String s) {
        if (s.indexOf("/") > -1) {
            s = s.substring(s.indexOf("/") + 1).trim();
        }
        Object oldDo = Model.getFacade().getDoActivity(st);
        if (oldDo == null) {
            Model.getStateMachinesHelper().setDoActivity(st,
                    buildNewCallAction(s));
        } else {
            updateAction(oldDo, s);
        }
    }

    /**
     * This builds a CallAction with default attributes. But without Operation!
     *
     * @author MVW
     * @param s
     *            string representing the Script of the Action
     * @return The newly created CallAction.
     */
    private Object buildNewCallAction(String s) {
        Object a =
            Model.getCommonBehaviorFactory().createCallAction();
        Object ae =
            Model.getDataTypesFactory().createActionExpression(LANGUAGE, s);
        Model.getCommonBehaviorHelper().setScript(a, ae);
        Model.getCoreHelper().setName(a, "anon");
        return a;
    }

    /**
     * Update an existing Action with a new Script.
     *
     * @author MVW
     * @param old the Action
     * @param s   a string representing a new Script for the ActionExpression
     */
    private void updateAction(Object old, String s) {
        Object ae = Model.getFacade().getScript(old); // the ActionExpression
        String language = LANGUAGE;
        if (ae != null) {
            language = Model.getDataTypesHelper().getLanguage(ae);
            String body = (String) Model.getFacade().getBody(ae);
            if (body.equals(s)) {
                return;
            }
        }
        ae = Model.getDataTypesFactory().createActionExpression(language, s);
        Model.getCommonBehaviorHelper().setScript(old, ae);
    }

    /**
     * This deletes modelelements, and swallows null without barking.
     *
     * @author Michiel
     * @param obj
     *            the modelelement to be deleted
     */
    private void delete(Object obj) {
        if (obj != null) {
            Model.getUmlFactory().delete(obj);
        }
    }

}
