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 @ 1066

History | View | Annotate | Download (31.1 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
import java.util.concurrent.locks.ReentrantLock;
19

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

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

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

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

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

    
73
    public static class OutputWriter extends Writer {
74

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

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

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

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

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

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

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

    
127
    }
128

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

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

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

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

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

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

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

    
218

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
478
    }
479

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
771
    class ScriptTask extends AbstractMonitorableTask {
772

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

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

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

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

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

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

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

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

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

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

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

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

    
939
}