Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.lib / org.gvsig.scripting.lib.impl / src / main / java / org / gvsig / scripting / impl / DefaultScriptingScript.java @ 1084

History | View | Annotate | Download (31 KB)

1
package org.gvsig.scripting.impl;
2

    
3
import groovy.lang.GroovyClassLoader;
4
import groovy.util.GroovyScriptEngine;
5
import java.io.File;
6
import java.io.IOException;
7
import java.io.InputStream;
8
import java.io.PrintStream;
9
import java.io.Writer;
10
import java.net.URL;
11
import java.nio.charset.Charset;
12
import java.util.ArrayList;
13
import java.util.Arrays;
14
import java.util.HashSet;
15
import java.util.LinkedHashSet;
16
import java.util.List;
17
import java.util.Set;
18

    
19
import javax.script.Compilable;
20
import javax.script.CompiledScript;
21
import javax.script.Invocable;
22
import javax.script.ScriptEngine;
23
import javax.script.ScriptException;
24

    
25
import org.apache.commons.io.FileUtils;
26
import org.apache.commons.io.FilenameUtils;
27
import org.apache.commons.io.IOUtils;
28
import org.apache.commons.lang3.StringUtils;
29
import org.apache.commons.lang3.exception.ExceptionUtils;
30
import org.gvsig.scripting.CompileErrorException;
31
import org.gvsig.scripting.ExecuteErrorException;
32
import org.gvsig.scripting.Main;
33
import org.gvsig.scripting.ScriptingBaseScript;
34
import org.gvsig.scripting.ScriptingFolder;
35
import org.gvsig.scripting.ScriptingManager;
36
import org.gvsig.scripting.ScriptingScript;
37
import org.gvsig.scripting.ScriptingUnit;
38
import org.gvsig.tools.dispose.Disposable;
39
import org.gvsig.tools.observer.Observer;
40
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
41
import org.gvsig.tools.task.AbstractMonitorableTask;
42
import org.ini4j.Ini;
43
import org.python.core.Py;
44
import org.python.core.PyException;
45
import org.python.core.PyString;
46
import org.python.core.PySystemState;
47
import org.python.core.PyTraceback;
48
import org.python.core.imp;
49
import org.python.jsr223.MyPyScriptEngine;
50
import org.slf4j.Logger;
51
import org.slf4j.LoggerFactory;
52

    
53
@SuppressWarnings("EqualsAndHashcode")
54
public class DefaultScriptingScript extends AbstractScript implements
55
        ScriptingScript {
56

    
57
    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
58
    private static final Logger logger = LoggerFactory.getLogger(DefaultScriptingScript.class);
59
    protected String langName;
60
    protected String extension = null;
61
    protected String librarySuffix = null;
62
    protected ScriptEngine engine = null;
63
    protected CompiledScript compiledCode;
64
    protected boolean useSysPath;
65

    
66
    private String code = null;
67
    private String mainName = "main";
68
    private final DelegateWeakReferencingObservable delegatedObservable;
69
    private OutputWriter stdout;
70
    private OutputWriter stderr;
71

    
72
    public static class OutputWriter extends Writer {
73

    
74
        private final Set<Writer> writers = new HashSet<>();
75
        private final PrintStream out;
76

    
77
        private OutputWriter(PrintStream out) {
78
            this.out = out;
79
        }
80

    
81
        @Override
82
        public void write(char[] cbuf, int off, int len) throws IOException {
83
            try {
84
                byte[] buf = new String(cbuf).getBytes(Charset.forName("UTF-8"));
85
                out.write(buf, off, len);
86
            } catch (Exception ex) {
87
                logger.warn("Can't output",ex);
88
            }
89
            for (Writer writer : writers) {
90
                try {
91
                    writer.write(cbuf, off, len);
92
                } catch (Exception ex) {
93
                    logger.warn("Can't output",ex);
94
                }
95
            }
96
        }
97

    
98
        @Override
99
        public void flush() throws IOException {
100
            try {
101
                out.flush();
102
            } catch (Exception ex) {
103
                logger.warn("Can't flush",ex);
104
            }
105
            for (Writer writer : writers) {
106
                try {
107
                    writer.flush();
108
                } catch (Exception ex) {
109
                    logger.warn("Can't flush",ex);
110
                }
111
            }
112
        }
113

    
114
        @Override
115
        public void close() throws IOException {
116
        }
117

    
118
        private void addWriter(Writer out) {
119
            this.writers.add(out);
120
        }
121

    
122
        private void removeWriter(Writer out) {
123
            this.writers.remove(out);
124
        }
125

    
126
    }
127

    
128
    @SuppressWarnings("OverridableMethodCallInConstructor")
129
    protected DefaultScriptingScript(ScriptingFolder parent, String typename, ScriptingManager manager, String id) {
130
        super(parent, typename, manager, id);
131
        this.useSysPath = false;
132
        this.setLangName("python");
133
        this.setSaved(true);
134
        this.delegatedObservable = new DelegateWeakReferencingObservable(this);
135
        this.stdout = new OutputWriter(System.out);
136
        this.stderr = new OutputWriter(System.err);
137
    }
138

    
139
    public DefaultScriptingScript(ScriptingFolder parent, ScriptingManager manager, String id) {
140
        this(parent, ScriptingManager.UNIT_SCRIPT, manager, id);
141
    }
142

    
143
    @Override
144
    public int hashCode() {
145
        File f = this.getFile();
146
        if( f!=null ) {
147
            return "#$FILE$#".hashCode() + f.getAbsolutePath().hashCode();
148
        }
149
        String s = this.getId();
150
        if( s != null ) {
151
            return "#$ID$#".hashCode() + s.hashCode();
152
        }
153
        s = this.getCode();
154
        if( s != null ) {
155
            return "#$CODE$#".hashCode() + s.hashCode();
156
        }
157
        return super.hashCode();
158
    }
159
    @Override
160
    public void addStdoutWriter(Writer out) {
161
        this.stdout.addWriter(out);
162
    }
163
    
164
    @Override
165
    public void addStderrWriter(Writer err) {
166
        this.stderr.addWriter(err);
167
    }
168
    
169
    @Override
170
    public void removeStdoutWriter(Writer out) {
171
        this.stdout.removeWriter(out);
172
    }
173
    
174
    @Override
175
    public void removeStderrWriter(Writer err) {
176
        this.stdout.removeWriter(err);
177
    }
178
    
179
    public Object __getattr__(String name) {
180
        try {
181
            ScriptEngine theEngine = this.getEngine();
182
            this.compile();
183
            return theEngine.get(name);
184
        } catch(Exception ex) {
185
            return null;
186
        }
187
    }
188

    
189
    public void __setattr__(String name, Object value) {
190
        ScriptEngine theEngine = this.getEngine();
191
        this.compile();
192
        theEngine.put(name, value);
193
    }
194

    
195
    public Object __call__() {
196
        return this.run();
197
    }
198

    
199
    public Object __call__(Object[] args) {
200
        return this.run(args);
201
    }
202

    
203
    public OutputWriter getStdout() {
204
        return this.stdout;
205
    }
206
    
207
    public OutputWriter getStderr() {
208
        return this.stderr;
209
    }
210
    
211
    protected void notifyErrors(Exception exception, String command) {
212
        this.delegatedObservable.notifyObservers(new BaseScriptingNotifycation(
213
                this, BaseScriptingNotifycation.RUNTIME_ERROR_NOTIFICATION,
214
                command, exception));
215
    }
216

    
217

    
218
    @Override
219
    public String getCode() {
220
        if (this.code == null) {
221
            File f = null;
222
            try {
223
                f = this.getFileResource(this.extension);
224
                this.code = FileUtils.readFileToString(f);
225
            } catch (IOException e) {
226
                String fname = (f == null) ? "(null)" : f.getAbsolutePath();
227
                logger.warn("Can't load code from file '" + fname + "'.");
228
            }
229
        }
230
        return this.code;
231
    }
232

    
233
    @Override
234
    public void setCode(String code) {
235
        this.code = code;
236
        this.engine = null;
237
        this.compiledCode = null;
238
        this.setSaved(false);
239
    }
240
    
241
    @Override
242
    public String getLibrarySuffix() {
243
        return this.librarySuffix;
244
    }
245

    
246
    @Override
247
    public void setLibrarySuffix(String librarySuffix) {
248
        this.librarySuffix = librarySuffix;
249
    }
250
    
251
    public List<File> getLibFolders() {
252
        List<File> folders = this.manager.getLibFolders();
253
        String suffix = this.getLibrarySuffix();
254
        if( suffix == null ) {
255
            return folders;
256
        }
257
        for( int i=0; i<folders.size(); i++) {
258
            File folder = folders.get(i);
259
            File f = new File(folder.getParentFile(),folder.getName()+suffix);
260
            if( f.exists() ) {
261
                folders.set(i, f);
262
            }
263
        }
264
        return folders;
265
    }
266
    
267
    protected String getCodeToInitializeEngine() {
268
        String initName = "org/gvsig/scripting/langs/"+this.getLangName().toLowerCase()+"/init.txt";
269
        try {
270
            InputStream template = this.getClass().getClassLoader().getResourceAsStream(initName);
271
            if( template == null ) {
272
                return null;
273
            }
274
            List<String> lines = IOUtils.readLines(template);
275
            return StringUtils.join(lines, "\n");
276
        } catch (Exception ex) {
277
            logger.warn("Can't load code to initialize the script from '"+initName+".",ex);
278
            return null;
279
        }
280
    }
281

    
282
    private List<URL> getClassPath() {
283
        Set<URL> classPath = new HashSet<>();
284
        
285
        try {
286
//            ClassLoader myloader = this.getClass().getClassLoader();
287
//            if( myloader instanceof URLClassLoader) {
288
//                classPath.addAll(Arrays.asList(((URLClassLoader) myloader).getURLs()));
289
//            }
290
            URL url;
291
            if( StringUtils.isEmpty(this.getIsolationGroup()) ) {
292
                url = this.getFile().getParentFile().getCanonicalFile().toURI().toURL();
293
                classPath.add(url);
294
            }
295
            
296
            url = this.getManager().getUserFolder().getFile().getCanonicalFile().toURI().toURL();
297
            classPath.add(url);
298

    
299
            List<File> folders = getLibFolders();
300
            if( folders!=null ) {
301
                for (File folder : folders) {
302
                    url = folder.getAbsoluteFile().toURI().toURL();
303
                    classPath.add(url);
304
                }
305
            }
306
            
307
            ScriptingFolder folders2 = getManager().getSystemFolder();
308
            if( folders2!=null ) {
309
                for (ScriptingUnit folder : folders2.getUnits()) {
310
                    url = folder.getFile().getCanonicalFile().toURI().toURL();
311
                    classPath.add(url);
312
                }
313
            }
314
        } catch(Exception ex) {
315
            
316
        }
317
        return new ArrayList<>(classPath);
318
    }
319
    
320
    private void addClassPathToEngine(ScriptEngine engine, List<URL> classPath) {
321
        if( engine instanceof MyPyScriptEngine ) {
322
            PySystemState sys = Py.getSystemState();
323
            if( this.useSysPath ) {
324
//                logger.info("Running without sys.classLoader, "+ this.getFile().getAbsolutePath()+".");
325
//                ReentrantLock importLock =sys.getImportLock();
326
//                importLock.lock();
327
//                try {
328
                    Set<PyString> paths = new LinkedHashSet<>();
329
                    for( int i=0; i<sys.path.size(); i++) {
330
                        String path = (String) sys.path.get(i);
331
                        if( !(path.equals("__pyclasspath__/") || path.equals("__classpath__")) ) {
332
                            paths.add(Py.newString(path));
333
                        }
334
                    }
335
                    for (URL url : classPath) {
336
                        String path = FileUtils.toFile(url).getAbsolutePath();
337
                        paths.add(Py.newString(path));
338
                    }
339
                    paths.add(Py.newString("__classpath__"));
340
                    paths.add(Py.newString("__pyclasspath__/"));
341
                    sys.path.addAll(paths);
342
                    
343
//                } finally {
344
//                    importLock.unlock();
345
//                }
346
            } else {
347
                ClassLoader loader = sys.getClassLoader();
348
                if( loader == null ) {
349
                    ClassLoader parentClassLoader = imp.getParentClassLoader();
350
                    loader = new MutableURLClassLoader(classPath, parentClassLoader);
351
                    sys.setClassLoader(loader);
352
                } else if( loader instanceof MutableURLClassLoader ) {
353
                    ((MutableURLClassLoader) loader).addUrls(classPath);
354
                }
355
            }
356

    
357
        } else if( engine instanceof GroovyScriptEngine ) {
358
            GroovyClassLoader loader = ((GroovyScriptEngine)engine).getGroovyClassLoader();
359
            Set<URL> urls = new HashSet(); 
360
            urls.addAll(Arrays.asList(loader.getURLs()));
361
            for (URL url : classPath) {
362
                if( ! urls.contains(url) ) {
363
                    loader.addURL(url);
364
                }
365
            }
366
        }
367
    }
368

    
369
    public ScriptEngine getEngine() {
370
        if (this.engine == null) {
371
            synchronized(this.getManager()) {
372
                ScriptEngine scriptEngine = this.manager.getEngineByLanguage(langName, this.getIsolationGroup());
373
                addClassPathToEngine(scriptEngine,getClassPath());
374
                
375
                scriptEngine.put("script", this);
376
                scriptEngine.put("Main", Main.class);
377
                scriptEngine.getContext().setWriter(stdout);
378
                scriptEngine.getContext().setErrorWriter(stderr);
379

    
380
                this.engine = scriptEngine;
381
                String theCode = this.getCodeToInitializeEngine();
382
                if (theCode != null) {
383
                    try {
384
                        this.engine.eval(theCode);
385
                    } catch (Exception ex) {
386
                        logger.warn("Can't initialize engine with the code:\n" + theCode, ex);
387
                    }
388
                }
389
            }
390
        }
391
        return this.engine;
392
    }
393

    
394
    @Override
395
    protected void loadInf(Ini prefs) {
396
        super.loadInf(prefs);
397

    
398
        this.setMainName((String) getInfValue(prefs, "Script", "main", "main"));
399
        this.setLangName((String) getInfValue(prefs, "Script", "Lang",this.getLangName()));
400
        this.setLibrarySuffix((String) getInfValue(prefs, "Script", "LibraryVersion",null));
401
        this.useSysPath = getInfBoolean(prefs, "Script", "useSysPath", false);
402
    }
403

    
404
    @Override
405
    public void load(ScriptingFolder folder, String id) {
406
        this.setId(id);
407
        this.setParent(folder);
408

    
409
        String theExtension = FilenameUtils.getExtension(id);
410
        if( theExtension != null ) {
411
            String language = this.manager.getLanguageOfExtension(theExtension);
412
            if( language != null ) {
413
                this.setLangName(language);
414
            }
415
            this.setExtension(theExtension);
416
        }
417
        File f = getFileResource(".inf");
418
        if (f.isFile()) {
419
            Ini prefs = null;
420
            try {
421
                prefs = new Ini(f);
422
            } catch (Exception e) {
423
                logger.warn("Can't load 'inf' file '" + f.getAbsolutePath() + "'.", e);
424
            }
425
            loadInf(prefs);
426
        }
427
        this.setCode(null);
428
        this.setSaved(true);
429
    }
430

    
431
    @Override
432
    public void save() {
433
        this.saveInfo();
434
        // Guardo el codigo en el fichero
435
        File fcode = this.getFileResource(this.getExtension());
436
        try {
437
            FileUtils.write(fcode, this.getCode());
438
        } catch (Exception e) {
439
            logger.warn("Can't write code to file '" + fcode.getAbsolutePath() + "'.", e);
440
        }
441
        this.setSaved(true);
442
    }
443

    
444
    private void saveInfo() {
445
        File f = getFileResource(".inf");
446
        if (!f.isFile()) {
447
            try {
448
                f.createNewFile();
449
            } catch (Exception e) {
450
                logger.warn("Can't create 'inf' file '" + f.getAbsolutePath() + "'.", e);
451
            }
452
        }
453
        Ini prefs = null;
454
        try {
455
            prefs = new Ini(f);
456
        } catch (Exception e) {
457
            logger.warn("Can't load 'inf' file '" + f.getAbsolutePath() + "'.", e);
458
        }
459
        save(prefs);
460
    }
461
    
462
    @Override
463
    protected void save(Ini prefs) {
464
        super.save(prefs);
465
        prefs.put("Script", "main", this.getMainName());
466
        prefs.put("Script", "Lang", this.getLangName());
467
        if( this.useSysPath ) {
468
            prefs.put("Script", "useSysPath", this.useSysPath);
469
        }
470
        try {
471
            prefs.store();
472
        } catch (IOException e) {
473
            String fname = (prefs.getFile() == null) ? "(null)" : prefs.getFile().getAbsolutePath();
474
            logger.warn("Can't save inf file (" + fname + ").", e);
475
        }
476

    
477
    }
478

    
479
    @Override
480
    public String getLangName() {
481
        return this.langName;
482
    }
483

    
484
    protected void setLangName(final String langName) {
485
        if( langName == null ) {
486
            return;
487
        }
488
        this.langName = langName;
489
        this.setExtension(this.manager.getExtensionOfLanguage(this.langName));
490
    }
491

    
492
    @Override
493
    public String[] getIconNames() {
494
        return new String[]{
495
            "scripting-icon-" + this.getLangName().toLowerCase(),
496
            "scripting-icon-" + this.getLangName().toLowerCase() + "-open"
497
        };
498
    }
499

    
500
    @Override
501
    public String getMainName() {
502
        return this.mainName;
503
    }
504

    
505
    @Override
506
    public void setMainName(final String mainName) {
507
        this.mainName = mainName;
508
    }
509
    
510
    @Override
511
    public List<File> getFiles() {
512
        List<File> l = new ArrayList<>();
513
        l.add(this.getScriptFile());
514
        return l;
515
    }
516

    
517
    public String getExtension() {
518
        return this.extension;
519
    }
520

    
521
    public void setExtension(final String extension) {
522
        if (!extension.startsWith(".")) {
523
            this.extension = "." + extension;
524
        } else {
525
            this.extension = extension;
526
        }
527
    }
528

    
529
    @Override
530
    public void addObserver(final Observer o) {
531
        this.delegatedObservable.addObserver(o);
532
    }
533

    
534
    @Override
535
    public void deleteObserver(final Observer o) {
536
        this.delegatedObservable.deleteObserver(o);
537
    }
538

    
539
    @Override
540
    public void deleteObservers() {
541
        this.delegatedObservable.deleteObservers();
542
    }
543

    
544
    @Override
545
    public void put(final String name, final Object value) {
546
        this.getEngine().put(name, value);
547
    }
548

    
549
    @Override
550
    public void compile() {
551
        if (this.compiledCode == null) {
552
            ScriptEngine theEngine = this.getEngine();
553
            if (theEngine instanceof Compilable) {
554
                try {
555
                    Compilable compilable = (Compilable) theEngine;
556
                    String theCode = this.getCode();
557
                    if( "python".equalsIgnoreCase(this.getLangName()) ) {
558
                        // If a Unicode string with a coding declaration is passed to compile(),
559
                        // a SyntaxError will be raised, but this is necessary to import from 
560
                        // another module, so we remove it.
561
                        // http://bugs.jython.org/issue1696
562
                        theCode = theCode.replaceFirst("^\\s*#([^:\\n]*)coding:","#$1 c-o-d-i-n-g:");
563
                    }
564
                    this.compiledCode = compilable.compile(theCode);
565
                    if( theEngine instanceof Invocable) {
566
                        this.compiledCode.eval();
567
                    }
568
                } catch (ScriptException e) {
569
                    Object[] location = this.getLocation(e);
570
                    CompileErrorException ce = new CompileErrorException(
571
                            e.getMessage(), 
572
                            (File) location[0], 
573
                            (int) location[1], 
574
                            (int) location[2], 
575
                            e
576
                    );
577
                    notifyErrors(ce, "compile");
578
                    throw ce;
579
                } catch (Throwable e) {
580
                    CompileErrorException ce = new CompileErrorException(e.getMessage(), this.getScriptFile(), e);
581
                    notifyErrors(new Exception(e), "compile");
582
                    throw ce;
583
                }
584
            } else {
585
                String theCode = this.getCode();
586
                try {
587
                    theEngine.eval(theCode);
588
                } catch (ScriptException e) {
589
                    Object[] location = this.getLocation(e);
590
                    CompileErrorException ce = new CompileErrorException(
591
                            e.getMessage(), 
592
                            (File) location[0], 
593
                            (int) location[1], 
594
                            (int) location[2], 
595
                            e
596
                    );                    
597
                    notifyErrors(ce, "compile");
598
                    throw ce;
599
                }
600
            }
601
        }
602
    }
603

    
604
    public void addDisposable(Disposable disposable) {
605
        //pass
606
    }
607

    
608
    /**
609
     * Run the main function of this script.
610
     * This method is created by familiarity when running the script from another script.
611
     * @return 
612
     */
613
    public Object main() {
614
        return this.run(null);
615
    }
616

    
617
    /**
618
     * Run the main function of this script.
619
     * This method is created by familiarity when running the script from another script.
620
     * @param args
621
     * @return 
622
     */
623
    public Object main(Object... args) {
624
        return this.run(args);
625
    }
626

    
627
        
628
    @Override
629
    public Object run() {
630
        return this.run(null);
631
    }
632

    
633
    @Override
634
    public Object run(Object args[]) {
635
        if( !this.isEnabled() ) {
636
            System.err.printf("The script '"+this.getName()+"' is not enabled, see properties page to change.\n");
637
            return null;
638
        }
639
        if (args == null) {
640
            args = new Object[]{};
641
        }
642
        this.compile();
643
        return this.invokeFunction(this.getMainName(), args);
644
    }
645

    
646
    @Override
647
    public Object invokeFunction(final String name, Object args[]) {
648
        try {
649
            if (this.getEngine() instanceof Invocable) {
650
                Invocable invocable = (Invocable) this.getEngine();
651
                this.compile();
652
                if (args == null) {
653
                    args = new Object[]{};
654
                }
655
                return invocable.invokeFunction(name, args);
656
            } else {
657
                if (this.compiledCode != null) {
658
                    Object x = this.compiledCode.eval();
659
                    if( x instanceof Main ) {
660
                        return ((Main) x).main(args);
661
                    } else if(x instanceof Runnable) {
662
                        ((Runnable) x).run();
663
                    }
664
                }
665
                return null;
666
            }
667
        } catch (ScriptException e) {
668
            Object[] location = this.getLocation(e);
669
            ExecuteErrorException ee = new ExecuteErrorException(
670
                    e.getMessage(), 
671
                    (File) location[0], 
672
                    (int) location[1], 
673
                    (int) location[2], 
674
                    e
675
            );
676
            notifyErrors(ee, "invoke");
677
            throw ee;
678

    
679
        } catch (Error | Exception e) {
680
            ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e);
681
            notifyErrors(ee, "invoke");
682
            throw ee;
683
        }
684
    }
685

    
686
    @Override
687
    public File getScriptFile() {
688
        return this.getFileResource(extension);
689
    }
690

    
691
    private Object[] getLocation(ScriptException e) {
692
        Throwable[] es = ExceptionUtils.getThrowables(e);
693
        Exception dbgex; // Para debug con mas comodidad
694
        for( Throwable t : es) {
695
            if( t instanceof PyException ) {
696
                try {
697
                    PyException pyex = (PyException)t;
698
                    PyTraceback tb = pyex.traceback;
699
                    if( tb!=null ) {
700
                        while( tb.tb_next!=null ) {
701
                            tb = (PyTraceback) tb.tb_next;
702
                        }
703
                        String s = tb.tb_frame.f_globals.__getitem__(new PyString("__file__")).asString();
704
                        if( s.endsWith("$py.class") ) {
705
                            s = s.substring(0, s.length()-9) + ".py";
706
                            File resource = new File(s);
707
                            return new Object[] { resource, tb.tb_lineno, 0};
708
                        }
709
                        return new Object[] { this.getScriptFile(), tb.tb_lineno, 0};
710
                    }
711
                } catch(Exception ex) {
712
                    // Pass
713
                    dbgex = ex;
714
                }
715
            }
716
        }        
717
        int column = e.getColumnNumber();
718
        if( column < 0 ) {
719
            column = 0;
720
        }
721
        return new Object[] { this.getScriptFile(), e.getLineNumber(), column };
722
    }
723
    
724
    
725
    @Override
726
    public Object invokeMethod(final Object obj, final String name, Object[] args)
727
            throws NoSuchMethodException {
728

    
729
        if (this.getEngine() instanceof Invocable) {
730
            Invocable invocable = (Invocable) this.getEngine();
731
            this.compile();
732
            if (args == null) {
733
                args = new Object[]{};
734
            }
735
            try {
736
                return invocable.invokeMethod(obj, name, args);
737
            } catch (ScriptException e) {
738
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e.getLineNumber(), e.getColumnNumber(), e);
739
                notifyErrors(ee, "invoke");
740
                throw ee;
741
            } catch (Throwable e) {
742
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e);
743
                notifyErrors(ee, "invoke");
744
                throw ee;
745
            }
746
        } else {
747
            return null;
748
        }
749
    }
750

    
751
    @Override
752
    public File getResource(String filename) {
753
        return new File(this.getParent().getFile(), filename);
754
    }
755

    
756
    @Override
757
    public String getMimeType() {
758
        return "text/"+ this.getLangName();
759
    }
760

    
761
    @Override
762
    protected void console_println(String s) {
763
        super.console_println(s);
764
        try {
765
            this.stdout.write(s+"\n");
766
        } catch (IOException ex) {
767
        }
768
    }
769

    
770
    class ScriptTask extends AbstractMonitorableTask {
771

    
772
        ScriptingBaseScript script = null;
773
        Object[] args = null;
774

    
775
        protected ScriptTask(ScriptingBaseScript script, Object[] args) {
776
            super(script.getName(), false);
777
            this.args = args;
778
            this.script = script;
779
            this.script.put("task", this);
780
            this.script.put("taskStatus", this.getTaskStatus());
781
        }
782

    
783
        @Override
784
        public void run() {
785
            try {
786
                console_println("Running script " + this.script.getName() + ".");
787
                script.run(this.args);
788
                console_println("Script " + this.script.getName() + " terminated.");
789
            } catch (Throwable e) {
790
                console_println("Stript " + this.script.getName() + " aborted.");
791
            } finally {
792
                this.taskStatus.terminate();
793
                try {
794
                    Thread.sleep(3000);
795
                } catch (InterruptedException e) {
796
                    // Ignore
797
                }
798
                this.taskStatus.remove();
799
            }
800
        }
801

    
802
        public void showTaskStatus() {
803
            this.taskStatus.add();
804
        }
805
    }
806

    
807
    @Override
808
    public void runAsTask(Object[] args) {
809
        if( !this.isEnabled() ) {
810
            System.err.printf("The script '"+this.getName()+"' is not enabled, see properties page to change.\n");
811
            return;
812
        }
813
        ScriptTask task = new ScriptTask(this, args);
814
        task.start();
815
    }
816

    
817
    @Override
818
    public boolean remove() {
819
        boolean r = true;
820
        File folder = this.getParent().getFile();
821
        File f = new File(folder, this.getId() + ".inf");
822
        try {
823
            FileUtils.forceDelete(f);
824
        } catch (IOException e) {
825
            logger.warn("Can't remove inf file '" + f.getAbsolutePath() + "'.", e);
826
            r = false;
827
        }
828
        try {
829
            f = new File(folder, this.getId() + this.getExtension());
830
            FileUtils.forceDelete(f);
831
        } catch (IOException e) {
832
            logger.warn("Can't remove code file '" + f.getAbsolutePath() + "'.", e);
833
            r = false;
834
        }
835
        return r;
836
    }
837

    
838
    @Override
839
    public void create(ScriptingFolder folder, String id, String language) {
840
        this.setParent(folder);
841
        this.setId(id);
842
        if (language == null) {
843
            this.setLangName("python");
844
        } else {
845
            this.setLangName(language);
846
        }
847
        this.setExtension(this.manager.getExtensionOfLanguage(getLangName()));
848

    
849
        File file = new File(folder.getFile(), id + ".inf");
850
        try {
851
            file.createNewFile();
852
        } catch (IOException e) {
853
            logger.warn("Can't create file of the dialog in '" + file.getAbsolutePath() + "'.", e);
854
        }
855
        File fcode = this.getFileResource(this.getExtension());
856
        if( fcode.exists() ) {
857
            this.saveInfo();
858
        } else {
859
            String template = this.getNewTemplate();
860
            if( template != null ) {
861
                this.setCode(template);
862
            }
863
            this.save();
864
        }
865
    }
866

    
867
    public String getNewTemplate() {
868
        String theTemplateName = "org/gvsig/scripting/langs/"+this.getLangName().toLowerCase()+"/new_template.txt";
869
        try {
870
            InputStream template = this.getClass().getClassLoader().getResourceAsStream(theTemplateName);
871
            if( template == null ) {
872
                return null;
873
            }
874
            List<String> lines = IOUtils.readLines(template);
875
            return StringUtils.join(lines, "\n");
876
        } catch (Exception ex) {
877
            logger.warn("Can't load new-template from '"+theTemplateName+"'.",ex);
878
            return null;
879
        }
880
    }
881
    
882
    public ScriptingUnit get(String name) {
883
        return this.manager.getScript(name);
884
    }
885

    
886
    public ScriptingUnit get(File file) {
887
        return this.manager.getScript(file);
888
    }
889

    
890
    @Override
891
    public boolean move(ScriptingFolder target) {
892
        if (! manager.validateUnitId(target, this.getId()) ) {
893
            logger.info("Can't move script '"+this.getId()+"' to '"+target.getFile().getAbsolutePath()+"', is not valid.");
894
            return false;
895
        }
896
        if( !this.isSaved() ) {
897
            logger.info("Can't move script '"+this.getId()+"', is not saved.");
898
            return false;
899
        }
900
        try {
901
            File codefile = this.getFileResource(this.extension);
902
            FileUtils.moveFileToDirectory(this.getFile(), target.getFile(),true);
903
            FileUtils.moveFileToDirectory(codefile, target.getFile(), true);
904
            this.parent = target;
905
            this.load(target, id);
906
        } catch (IOException ex) {
907
            logger.info("Can't move script '"+this.getId()+"' to '"+target.getFile().getAbsolutePath()+"', "+ex.getMessage(),ex);
908
            return false;
909
        }
910
        return true;
911
    }
912

    
913
    @Override
914
    public boolean rename(String newId) {
915
        if (! manager.validateUnitId(this.getParent(), newId) ) {
916
            logger.info("Can't rename script '"+this.getId()+"', target id '"+newId+"' is not valid.");
917
            return false;
918
        }
919
        if( !this.isSaved() ) {
920
            logger.info("Can't rename script '"+this.getId()+"', is not saved.");
921
            return false;
922
        }
923
        try {
924
            ScriptingFolder target = this.getParent();
925
            File codefile = this.getFileResource(this.extension);
926
            FileUtils.moveFile(this.getFile(),  new File(target.getFile(),newId+".inf") );
927
            FileUtils.moveFile(codefile, new File(target.getFile(),newId+this.extension));
928
            this.setId(newId);
929
            this.saveInfo();
930
            this.load(target, id);
931
        } catch (IOException ex) {
932
            logger.info("Can't rename script '"+this.getId()+"' to '"+newId+"', "+ex.getMessage(),ex);
933
            return false;
934
        }        
935
        return true;
936
    }
937

    
938
}