source: trunk/org.modelica.mdt.omc/src/org/modelica/mdt/omc/OMCProxy.java @ 325

Last change on this file since 325 was 325, checked in by remar, 19 years ago

org.modelica.mdt.core

fixed a comment, removed some debug printouts

org.modelica.mdt.omc

getErrorString() is called after every call to sendExpression()

org.modelica.mdt.ui

beutification fixes

org.modelica.mdt.test

added some assertions

File size: 21.4 KB
Line 
1/*
2 * This file is part of Modelica Development Tooling.
3 *
4 * Copyright (c) 2005, Link�pings universitet, Department of
5 * Computer and Information Science, PELAB
6 *
7 * All rights reserved.
8 *
9 * (The new BSD license, see also
10 * http://www.opensource.org/licenses/bsd-license.php)
11 *
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are
15 * met:
16 *
17 * * Redistributions of source code must retain the above copyright
18 *   notice, this list of conditions and the following disclaimer.
19 *
20 * * Redistributions in binary form must reproduce the above copyright
21 *   notice, this list of conditions and the following disclaimer in
22 *   the documentation and/or other materials provided with the
23 *   distribution.
24 *
25 * * Neither the name of Link�pings universitet nor the names of its
26 *   contributors may be used to endorse or promote products derived from
27 *   this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 */
41
42package org.modelica.mdt.omc;
43
44import java.io.BufferedReader;
45import java.io.File;
46import java.io.FileReader;
47import java.io.IOException;
48import java.util.Collection;
49import java.util.LinkedList;
50import java.util.StringTokenizer;
51
52import org.eclipse.core.resources.IFile;
53import org.eclipse.core.runtime.Platform;
54import org.modelica.mdt.core.IModelicaClass;
55import org.modelica.mdt.core.List;
56import org.modelica.mdt.core.ListElement;
57import org.modelica.mdt.core.compiler.ConnectException;
58import org.modelica.mdt.core.compiler.ElementsInfo;
59import org.modelica.mdt.core.compiler.IModelicaCompiler;
60import org.modelica.mdt.core.compiler.InvocationError;
61import org.modelica.mdt.core.compiler.ModelicaParser;
62import org.modelica.mdt.core.compiler.UnexpectedReplyException;
63import org.modelica.mdt.internal.core.ElementLocation;
64import org.modelica.mdt.internal.core.ErrorManager;
65import org.modelica.mdt.omc.internal.ParseResults;
66import org.modelica.mdt.omc.internal.OMCParser;
67import org.modelica.mdt.omc.internal.corba.OmcCommunication;
68import org.modelica.mdt.omc.internal.corba.OmcCommunicationHelper;
69import org.omg.CORBA.ORB;
70
71/**
72 * The OMCProxy is the glue between the OpenModelica Compiler and MDT.
73 * It uses the interactive API of OMC to get information about classes
74 * and to load classes into OMC.
75 *
76 * @author Andreas Remar
77 */
78public class OMCProxy implements IModelicaCompiler
79{
80    /* the CORBA object */
81    private static OmcCommunication omcc;
82   
83    /* what Operating System we're running on */
84    private static String os;
85   
86    /* indicates if we've setup the communication with OMC */
87    private boolean hasInitialized = false;
88   
89    /* indicates if the Modelica System Library has been loaded */
90    private boolean systemLibraryLoaded = false;
91
92    private String[] standardLibraryPackages = { "Modelica" };
93
94    /* should we trace the calls to sendExpression? */
95    private static boolean traceOMCCalls = false;
96    private static boolean traceOMCStatus = false;
97    static
98    {
99        /* load debug options and set debug flag variables accordingly */
100       
101       
102        String value = Platform.getDebugOption  /*load trace/omcCalls flag */
103            ("org.modelica.mdt.omc/trace/omcCalls");
104        if (value != null && value.equalsIgnoreCase("true"))
105        {
106            traceOMCCalls = true;
107        }
108       
109        value = Platform.getDebugOption
110        ("org.modelica.mdt.omc/trace/omcStatus");
111        if (value != null && value.equalsIgnoreCase("true"))
112        {
113            traceOMCStatus = true;
114        }
115    }
116   
117    public OMCProxy()
118    {
119       
120    }
121
122    /**
123     * Reads in the OMC CORBA object reference from a file on disk.
124     * @return the object reference as a <code>String</code>
125     */
126    private static String readObjectFromFile() throws ConnectException
127    {
128        File f = new File(getPathToObject());
129        String stringifiedObjectReference = null;
130
131        BufferedReader br = null;
132        FileReader fr = null;
133        try
134        {
135            fr = new FileReader(f);
136        }
137        catch(IOException e)
138        {
139            throw new ConnectException
140                ("Unable to read OpenModelica Compiler CORBA object from "
141                        + f.toString());
142        }
143
144        br = new BufferedReader(fr);
145           
146        try
147        {
148            stringifiedObjectReference = br.readLine();
149        }
150        catch(IOException e)
151        {
152            throw new ConnectException("Unable to read OpenModelica Compiler"
153                    + " CORBA object from " + getPathToObject());
154        }
155        return stringifiedObjectReference;
156    }
157   
158    /**
159     * @return Returns the path to the OMC CORBA object that is stored on disk.
160     */
161    private static String getPathToObject()
162    {
163        String fileName = null;
164        if(os.equals("Unix"))
165        {
166            /* This mirrors the way OMC creates the object file. */
167            String username = System.getenv("USER");
168            if(username == null)
169            {
170                username = "nobody";
171            }
172            fileName = "/tmp/openmodelica." + username + ".objid";
173        }
174        else if(os.equals("Windows"))
175        {
176            String temp = System.getenv("TMP");         
177            fileName = temp + "\\openmodelica.objid";
178        }
179       
180        logOMCStatus("will look for OMC object reference in '" 
181                + fileName + "'");
182       
183        return fileName;
184    }
185   
186    /**
187     * Start a new OMC server.
188     */
189    private static void startServer() throws ConnectException
190    {
191        String pathToOmc = null;
192
193        /*
194         * Path to omc (or omc.exe) can be found in the OPENMODELICAHOME
195         * variable.
196         */
197        String omHome = System.getenv("OPENMODELICAHOME");
198        if(omHome == null)
199        {
200            final String m = "Environment variable OPENMODELICAPATH not set";
201            logOMCStatus("Environment variable OPENMODELICAPATH not set,"+
202                    " don't know how to start OMC");
203            throw new ConnectException(m);
204        }
205       
206        if(os.equals("Unix"))
207        {
208            pathToOmc = omHome + "/omc";
209        }
210        else if(os.equals("Windows"))
211        {
212            pathToOmc = omHome + "\\omc.exe";
213        }
214
215        /*
216         * Delete old object reference file. We need to do this because we're
217         * checking if the file exists to determine if the server has started
218         * or not (further down).
219         */
220        File f = new File(getPathToObject());
221        if(f.exists())
222        {
223            logOMCStatus("removing old OMC object reference file");
224            f.delete();
225        }
226       
227        String command[] = { pathToOmc, "+d=interactiveCorba" };
228        try
229        {
230            logOMCStatus("running command " + command[0] + " " + command[1]);
231            Runtime.getRuntime().exec(command);
232            logOMCStatus("command run successfully");
233        }
234        catch(IOException e)
235        {
236            /*
237             * If we fail to start the compiler, maybe the executable is in
238             * the Compiler directory (if we've compiled the compiler from
239             * source). Try starting OMC from this secondary location.
240             */
241            logOMCStatus("error running command " + e.getMessage()
242                    + "\ntrying allternative path to the binary");
243            String secondaryPathToOmc = null;
244            try
245            {
246                if(os.equals("Unix"))
247                {
248                    secondaryPathToOmc = omHome + "/Compiler/omc";
249                }
250                else if(os.equals("Windows"))
251                {
252                    secondaryPathToOmc = omHome + "\\Compiler\\omc.exe";
253                }
254
255                command = 
256                    new String[]{secondaryPathToOmc, "+d=interactiveCorba"};
257                logOMCStatus("running command " 
258                        + command[0] + " " + command[1]);
259                Runtime.getRuntime().exec(command);
260                logOMCStatus("command run successfully");
261            }
262            catch(IOException ex)
263            {
264                logOMCStatus("error running command, giving up"); 
265                throw new ConnectException
266                    ("Unable to start Open Modelica Compiler. "
267                     + "Tried starting " + pathToOmc
268                     + " and " + secondaryPathToOmc);
269            }
270        }
271
272        /*
273         * Wait until the object exists on disk, but if it takes longer than
274         * 5 seconds, abort. (Very arbitrary 5 seconds..)
275         */
276        int ticks = 0;
277        while(!f.exists())
278        {
279            try
280            {
281                Thread.sleep(100);
282            }
283            catch(InterruptedException e)
284            {
285                // Ignore
286            }
287            ticks++;
288           
289            /* If we've waited for 5 seconds, abort the wait for OMC */
290            if(ticks > 50)
291            {
292                logOMCStatus("no OMC object reference file created after " + 
293                        "approximatly 5 seconds\n" +
294                        "it seems OMC does not want to come up, giving up");
295                throw new ConnectException
296                    ("Unable to start the Open Modelica Compiler. Waited for 5"
297                            +" seconds, but it didn't respond.");
298            }
299        }
300    }
301   
302    /**
303     * Initializes an ORB, converts the stringified OMC object to a real
304     * CORBA object, and then narrows that object to an OmcCommunication
305     * object.
306     */
307    private static void setupOmcc(String stringifiedObjectReference)
308    {
309        /* Can't remember why this is needed. But it is. */
310        String args[] = {null};
311       
312        ORB orb;
313        orb = ORB.init(args, null);
314       
315        /* Convert string to object. */
316        org.omg.CORBA.Object obj
317            = orb.string_to_object(stringifiedObjectReference);
318       
319        /* Convert object to OmcCommunication object. */
320        omcc = OmcCommunicationHelper.narrow(obj);
321    }
322   
323    /**
324     * @return the name of the operating system. If an unknown os is found,
325     * the default is Unix.
326     */
327    private static String getOs()
328    {
329        String osName = System.getProperty("os.name");
330        if(osName.contains("Linux"))
331        {
332            return "Unix";
333        }
334        else if(osName.contains("Windows"))
335        {
336            return "Windows";
337        }
338        else
339        {
340            ErrorManager.logWarning("'" + osName + "' is unsupported OS");
341            /* If the OS is not GNU/Linux or Windows, default to Unix */
342            return "Unix";
343        }
344    }
345
346    /**
347     * Initialize the communication with OMC
348     * @throws ConnectException if we're unable to start communicating with
349     * the server
350     */
351    private void init() throws ConnectException
352    {
353        /*
354         * Get type of operating system, used for finding object
355         * reference and starting OMC if the reference is faulty
356         */
357        os = getOs();
358       
359        /* See if an OMC server is already running */
360        File f = new File(getPathToObject());
361        String stringifiedObjectReference = null;
362        if(!f.exists())
363        {
364            /* If a server isn't running, start it */
365            logOMCStatus("no OMC object reference found");
366            startServer();
367        }
368        else
369        {
370            logOMCStatus("OMC object reference present," +
371                    " assuming OMC is running");
372        }
373       
374        /* Read in the CORBA OMC object from a file on disk */
375        stringifiedObjectReference = readObjectFromFile();
376
377        /*
378         * Setup up OMC object reference by initializing ORB and then
379         * converting the string object to a real CORBA object.
380         */
381        setupOmcc(stringifiedObjectReference);
382
383        try
384        {
385            /*
386             * Test the server by trying to send an expression to it.
387             * This might fail if the object reference found on disk didn't
388             * have a corresponding server running. If a server is missing,
389             * catch an exception and try starting a server.
390             */
391            logOMCStatus("trying to send expression to OMC");
392            omcc.sendExpression("1+1");
393            logOMCStatus("expression send successfully");
394        }
395        catch(org.omg.CORBA.COMM_FAILURE e)
396        {
397            /* Start server and set up omcc */
398            logOMCStatus("failed sending expression");
399            startServer();
400            stringifiedObjectReference = readObjectFromFile();
401            setupOmcc(stringifiedObjectReference);
402
403            try
404            {
405                /* Once again try to send an expression to OMC. If it fails this
406                 * time it's time to send back an exception to the caller of
407                 * this function. */
408                logOMCStatus("trying to send expression to OMC");
409                omcc.sendExpression("1+1");
410                logOMCStatus("expression send successfully");
411            }
412            catch(org.omg.CORBA.COMM_FAILURE x)
413            {
414                logOMCStatus("failed sending expression, giving up");
415                throw new ConnectException("Unable to start the OpenModelica"
416                        +" Compiler.");
417            }
418        }
419
420        hasInitialized = true;
421    }
422   
423    /**
424     * Send expression to OMC. If communication is not initialized, it
425     * is initialized here.
426     * @param exp the expression to send to OMC
427     * @throws ConnectException if we're unable to start communicating with
428     * the server
429     */
430    // TODO add synchronization so that two threads don't fudge up each others
431    // communication with OMC
432    // old synchronization aka 'private synchronized String sendExpression(String exp)'
433    // doesnt work when there is possibility of multiple instances of OMCProxy objects
434    private String sendExpression(String exp)
435        throws ConnectException
436    {
437        String retval = null;
438       
439        if(hasInitialized == false)
440        {
441            init();
442        }
443       
444        try
445        {
446            logOMCCall(exp);
447            retval = omcc.sendExpression(exp);
448            logOMCReply(retval);
449        }
450        catch(org.omg.CORBA.COMM_FAILURE x)
451        {
452            /* lost connection to OMC or something */
453            throw new ConnectException("Couldn't send expression to the "+
454                    "OpenModelica Compiler. Tried sending: " + exp);
455        }
456       
457        return retval;
458    }
459   
460    /**
461     * Logs the expression sent to OMC if the
462     * tracing flag (traceOMCCalls) is set
463     *
464     * @param expression the expression that is about to be sent to OMC
465     */
466    private static void logOMCCall(String expression)
467    {
468        if (!traceOMCCalls)
469        {
470            return;
471        }
472        System.out.println(">> " + expression);
473    }
474   
475    /**
476     * loggs the message conserning OMC status if the
477     * tracing flag traceOMCStatus is set
478     * @param message the message to log
479     */
480    private static void logOMCStatus(String message)
481    {
482        if (!traceOMCStatus)
483        {
484            return;
485        }
486        System.out.println("OMCSTATUS: " + message);
487    }
488
489    /**
490     * Logs the reply received from OMC if
491     * the tracing flag (traceOMCCalls) is set
492     *
493     * @param reply the reply recieved from the OMC
494     */
495    private static void logOMCReply(String reply)
496    {
497        if (!traceOMCCalls)
498        {
499            return;
500        }
501
502        StringTokenizer tokenizer = new StringTokenizer(reply, "\n");
503       
504        while (tokenizer.hasMoreTokens())
505        {
506            System.out.println("<< " + tokenizer.nextToken());
507        }
508    }
509   
510    /**
511     * Get the classes contained in a class (a package is a class..)
512     *
513     *
514     * @param className full class name where to look for packages
515     * @return an array of subclasses defined (and loaded into OMC)
516     *  inside the class named className, but don't return packages in this
517     *  class. The results is returned as Vector of objects but objects
518     *  are actually String's.
519     * 
520     * @throws ConnectException
521     * @throws UnexpectedReplyException
522     * @throws InitializationException
523     */ 
524    public List getClassNames(String className)
525        throws ConnectException, UnexpectedReplyException
526    {
527        String retval = sendExpression("getClassNames("+className+")");
528       
529        /* fetch error string but ignore it */
530        getErrorString();
531       
532        return ModelicaParser.parseList(retval);
533    }
534
535    /**
536     * Gets the restriction type of a class.
537     *
538     * @param className fully qualified class name
539     * @return the restriction type of the class or Type.CLASS if
540     *         type can't be determined
541     * @throws ConnectException
542     */
543    public IModelicaClass.Type getRestrictionType(String className)
544        throws ConnectException
545    {
546        String reply = 
547            sendExpression("getClassRestriction(" + className + ")");
548       
549        /* remove " around the reply */
550        reply = reply.trim();
551        reply = reply.substring(1, reply.length()-1);
552       
553        /* fetch error string but ignore it */
554        getErrorString();
555       
556        return IModelicaClass.Type.parse(reply);
557    }
558   
559    /**
560     * Fetches the error string from OMC. This should be called after an "Error"
561     * is received.
562     * @return
563     * @throws ConnectException
564     */
565    private String getErrorString()
566        throws ConnectException
567    {
568        String res = sendExpression("getErrorString()");
569        if(res != null)
570        {
571            res = res.trim();
572            return res.substring(1, res.length() - 1);
573        }
574        else
575            return "";
576    }
577   
578
579    /**
580     * Tries to load file into OMC which causes it to be parsed and the syntax
581     * checked.
582     * @param file the file we want to load
583     * @return either returns the classes (and packages) found in the file or
584     * the error messages from OMC
585     * @throws ConnectException
586     * @throws UnexpectedReplyException
587     * @throws InitializationException
588     */
589    public ParseResults loadSourceFile(IFile file)
590        throws ConnectException, UnexpectedReplyException
591    {
592        ParseResults res = new ParseResults();
593
594        String fullName = file.getLocation().toString();
595        String retval = 
596            sendExpression("loadFileInteractiveQualified(\"" + fullName + "\")");
597       
598        /* Always keep your stuff nice and tidy! */
599        retval = retval.trim();
600       
601        /*
602         * See if there were parse errors, an empty list {} also denotes error
603         */
604        if(retval.toLowerCase().contains("error") || retval.equals("{}"))
605        {           
606            res.setCompileErrors
607                (OMCParser.parseErrorString(getErrorString()));
608            res.setClassNames(new List());
609        }
610        /*
611         * file loaded and parse successsfully
612         */
613        else
614        {
615            res.setClassNames(ModelicaParser.parseList(retval));
616        }
617
618        /*
619         * If there were errors, but the compilation went through,
620         * collect the error messages. (Test if errorString != "")
621         */
622        String errorString = getErrorString();
623        System.out.println("errorString: -->" + errorString + "<--");
624        if(errorString.equals("") == false)
625        {
626//          errorString = errorString.trim();
627//          errorString = errorString.substring(1, errorString.length() - 1);
628            System.out.println("setCompileErrors!!!");
629            res.setCompileErrors(OMCParser.parseErrorString(errorString));
630        }
631       
632        return res;
633    }
634
635    /**
636     * Gets the location (file, line number and column number) of a Modelica
637     * element.
638     * @param className the element we want to get location of
639     * @return an IElementLocation containing the file, line number and column
640     * number of the given class
641     * @throws ConnectException
642     * @throws UnexpectedReplyException
643     * @throws InvocationError
644     */
645    public ElementLocation getClassLocation(String className)
646        throws ConnectException, UnexpectedReplyException, InvocationError
647    {
648        String retval = sendExpression("getCrefInfo(" + className + ")");
649       
650        /* fetch error string but ignore it */
651        getErrorString();
652       
653        if(retval.contains("Error") || retval.contains("error"))
654        {
655            throw new 
656                InvocationError("fetching file position of " + className,
657                        "getCrefInfo(" + className + ")");
658        }
659       
660       
661        /*
662         * The getCrefInfo reply have the following format:
663         *
664         * <file path>,<something>,<start line>,<start column>,<end line>,<end column>
665         *
666         * for example:
667         * /foo/Modelica/package.mo,writable,1,1,1029,13
668         */
669
670        /* For some reason, the list returned doesn't contain curly braces. */
671        retval = retval.trim();
672        retval = "{" + retval + "}"; 
673
674        List tokens = ModelicaParser.parseList(retval);
675       
676        String filePath = tokens.elementAt(0).toString();
677        int startLine;
678        int startColumn;
679        int endLine;
680        int endColumn;
681
682        try
683        {
684            startLine = Integer.parseInt(tokens.elementAt(2).toString());
685            startColumn = Integer.parseInt(tokens.elementAt(3).toString());
686            endLine = Integer.parseInt(tokens.elementAt(4).toString());
687            endColumn = Integer.parseInt(tokens.elementAt(5).toString());
688        }
689        catch (NumberFormatException e)
690        {
691            throw new 
692                UnexpectedReplyException("Can't parse getCrefInfo() reply, "+
693                                         "unexpected format");
694        }
695       
696        return new ElementLocation(filePath, 
697                    startLine, startColumn, endLine, endColumn);
698    }
699   
700    /**
701     * Queries the compiler if a particular modelica class/package is a package.
702     *
703     * @param className fully qualified name of the class/package
704     * @return true if className is a package false otherwise
705     * @throws ConnectException
706     */
707    public boolean isPackage(String className)
708        throws ConnectException
709    {
710        String retval = sendExpression("isPackage(" + className + ")");
711
712        /* fetch error string but ignore it */
713        getErrorString();
714       
715        return retval.contains("true");
716    }
717   
718    public Collection<ElementsInfo> getElementsInfo(String className)
719        throws ConnectException, InvocationError, UnexpectedReplyException
720    {
721        String retval = sendExpression("getElementsInfo("+ className +")");
722       
723        /* fetch error string but ignore it */
724        getErrorString();
725       
726        /*
727         * we need a efficient way to check if the result is
728         * humongosly huge list or 'Error' or maybe 'error'
729         */
730        for (int i = 0; i < retval.length(); i++)
731        {
732            if (retval.charAt(i) == '{')
733            {
734                /*
735                 * we found the begining of the list, send it to parser and
736                 * hope for the best
737                 */
738                List parsedList = ModelicaParser.parseList(retval);
739               
740                /* convert the parsedList to a collection of ElementsInfo:s */
741                LinkedList<ElementsInfo> elementsInfo = 
742                    new LinkedList<ElementsInfo>();
743
744                for (ListElement element : parsedList)
745                {
746                    elementsInfo.add(new ElementsInfo((List)element));
747                }
748               
749                return elementsInfo;
750            }
751            else if (retval.charAt(i) == 'E' || retval.charAt(i) == 'e')
752            {
753                /*
754                 * this is the unreadable way to check if the retval
755                 * equals 'Error' or 'error'
756                 */
757                if (retval.substring(i+1,i+5).equals("rror"))
758                {
759                    throw new 
760                        InvocationError("fetching contents of " + className,
761                                "getElementsInfo("+ className +")");
762                }
763                else
764                {
765                    /* OMC returned someting wierd, panic mode ! */
766                    break;
767                }
768            }
769        }
770        /* we have no idea what OMC returned */
771        throw new UnexpectedReplyException("getElementsInfo("+ className +")" + 
772                        "replies:'" + retval + "'");
773    }
774
775   
776    public String getCompilerName()
777    {
778        return "OpenModelica Compiler";
779    }
780
781    /**
782     * Loads in the Modelica System Library and returns names of the top-level
783     * packages.
784     * 
785     * @throws ConnectException if we're unable to start communicating with
786     * the server
787     */ 
788    public String[] getStandardLibrary() throws ConnectException
789    {
790        if (!systemLibraryLoaded)
791        {
792            sendExpression("loadModel(Modelica)");
793           
794            /* fetch error string but ignore it */
795            getErrorString();
796           
797            systemLibraryLoaded = true;
798        }
799
800        return standardLibraryPackages;
801    }
802}
Note: See TracBrowser for help on using the repository browser.