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

History | View | Annotate | Download (31.6 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
    public DefaultScriptingScript(ScriptingFolder parent, ScriptingManager manager, String id, String langName) {
144
        this(parent, ScriptingManager.UNIT_SCRIPT, manager, id);
145
        if( !StringUtils.isBlank(langName) ) {
146
            this.setLangName(langName);
147
        }
148
    }
149

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

    
196
    public void __setattr__(String name, Object value) {
197
        ScriptEngine theEngine = this.getEngine();
198
        this.compile();
199
        theEngine.put(name, value);
200
    }
201

    
202
    public Object __call__() {
203
        return this.run();
204
    }
205

    
206
    public Object __call__(Object[] args) {
207
        return this.run(args);
208
    }
209

    
210
    public OutputWriter getStdout() {
211
        return this.stdout;
212
    }
213
    
214
    public OutputWriter getStderr() {
215
        return this.stderr;
216
    }
217
    
218
    protected void notifyErrors(Exception exception, String command) {
219
        this.delegatedObservable.notifyObservers(new BaseScriptingNotifycation(
220
                this, BaseScriptingNotifycation.RUNTIME_ERROR_NOTIFICATION,
221
                command, exception));
222
    }
223

    
224

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

    
240
    @Override
241
    public void setCode(String code) {
242
        this.code = code;
243
        this.engine = null;
244
        this.compiledCode = null;
245
        this.setSaved(false);
246
    }
247
    
248
    @Override
249
    public String getLibrarySuffix() {
250
        return this.librarySuffix;
251
    }
252

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

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

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

    
364
        } else if( engine instanceof GroovyScriptEngine ) {
365
            GroovyClassLoader loader = ((GroovyScriptEngine)engine).getGroovyClassLoader();
366
            Set<URL> urls = new HashSet(); 
367
            urls.addAll(Arrays.asList(loader.getURLs()));
368
            for (URL url : classPath) {
369
                if( ! urls.contains(url) ) {
370
                    loader.addURL(url);
371
                }
372
            }
373
        }
374
    }
375

    
376
    public ScriptEngine getEngine() {
377
        if (this.engine == null) {
378
            synchronized(this.getManager()) {
379
                ScriptEngine scriptEngine = this.manager.getEngineByLanguage(langName, this.getIsolationGroup());
380
                addClassPathToEngine(scriptEngine,getClassPath());
381
                
382
                scriptEngine.put("script", this);
383
                scriptEngine.put("Main", Main.class);
384
                scriptEngine.getContext().setWriter(stdout);
385
                scriptEngine.getContext().setErrorWriter(stderr);
386

    
387
                this.engine = scriptEngine;
388
                String theCode = this.getCodeToInitializeEngine();
389
                if (theCode != null) {
390
                    try {
391
                        this.engine.eval(theCode);
392
                    } catch (Exception ex) {
393
                        logger.warn("Can't initialize engine with the code:\n" + theCode, ex);
394
                    }
395
                }
396
            }
397
        }
398
        return this.engine;
399
    }
400

    
401
    @Override
402
    protected void loadInf(Ini prefs) {
403
        super.loadInf(prefs);
404

    
405
        this.setMainName((String) getInfValue(prefs, "Script", "main", "main"));
406
        this.setLangName((String) getInfValue(prefs, "Script", "Lang",this.getLangName()));
407
        this.setLibrarySuffix((String) getInfValue(prefs, "Script", "LibraryVersion",null));
408
        this.useSysPath = getInfBoolean(prefs, "Script", "useSysPath", false);
409
    }
410

    
411
    @Override
412
    public void load(ScriptingFolder folder, String id) {
413
        this.setId(id);
414
        this.setParent(folder);
415

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

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

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

    
484
    }
485

    
486
    @Override
487
    public String getLangName() {
488
        return this.langName;
489
    }
490

    
491
    protected void setLangName(final String langName) {
492
        if( langName == null ) {
493
            return;
494
        }
495
        this.langName = langName;
496
        this.setExtension(this.manager.getExtensionOfLanguage(this.langName));
497
    }
498

    
499
    @Override
500
    public String[] getIconNames() {
501
        return new String[]{
502
            "scripting-icon-" + this.getLangName().toLowerCase(),
503
            "scripting-icon-" + this.getLangName().toLowerCase() + "-open"
504
        };
505
    }
506

    
507
    @Override
508
    public String getMainName() {
509
        return this.mainName;
510
    }
511

    
512
    @Override
513
    public void setMainName(final String mainName) {
514
        this.mainName = mainName;
515
    }
516
    
517
    @Override
518
    public List<File> getFiles() {
519
        List<File> l = new ArrayList<>();
520
        l.add(this.getScriptFile());
521
        return l;
522
    }
523

    
524
    public String getExtension() {
525
        return this.extension;
526
    }
527

    
528
    public void setExtension(final String extension) {
529
        if (!extension.startsWith(".")) {
530
            this.extension = "." + extension;
531
        } else {
532
            this.extension = extension;
533
        }
534
    }
535

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

    
541
    @Override
542
    public void deleteObserver(final Observer o) {
543
        this.delegatedObservable.deleteObserver(o);
544
    }
545

    
546
    @Override
547
    public void deleteObservers() {
548
        this.delegatedObservable.deleteObservers();
549
    }
550

    
551
    @Override
552
    public void put(final String name, final Object value) {
553
        this.getEngine().put(name, value);
554
    }
555

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

    
611
    public void addDisposable(Disposable disposable) {
612
        //pass
613
    }
614

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

    
624
    /**
625
     * Run the main function of this script.
626
     * This method is created by familiarity when running the script from another script.
627
     * @param args
628
     * @return 
629
     */
630
    public Object main(Object... args) {
631
        return this.run(args);
632
    }
633

    
634
        
635
    @Override
636
    public Object run() {
637
        return this.run(null);
638
    }
639

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

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

    
686
        } catch (Error | Exception e) {
687
            ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e);
688
            notifyErrors(ee, "invoke");
689
            throw ee;
690
        }
691
    }
692

    
693
    @Override
694
    public File getScriptFile() {
695
        return this.getFileResource(extension);
696
    }
697

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

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

    
758
    @Override
759
    public File getResource(String filename) {
760
        return new File(this.getParent().getFile(), filename);
761
    }
762

    
763
    @Override
764
    public String getMimeType() {
765
        return "text/"+ this.getLangName();
766
    }
767

    
768
    @Override
769
    protected void console_println(String s) {
770
        super.console_println(s);
771
        try {
772
            this.stdout.write(s+"\n");
773
        } catch (IOException ex) {
774
        }
775
    }
776

    
777
    class ScriptTask extends AbstractMonitorableTask {
778

    
779
        ScriptingBaseScript script = null;
780
        Object[] args = null;
781

    
782
        protected ScriptTask(ScriptingBaseScript script, Object[] args) {
783
            super(script.getName(), false);
784
            this.args = args;
785
            this.script = script;
786
            this.script.put("task", this);
787
            this.script.put("taskStatus", this.getTaskStatus());
788
        }
789

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

    
809
        public void showTaskStatus() {
810
            this.taskStatus.add();
811
        }
812
    }
813

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

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

    
845
    @Override
846
    public void create(ScriptingFolder folder, String id, String language) {
847
        this.setParent(folder);
848
        this.setId(id);
849
        if (language == null) {
850
            this.setLangName("python");
851
        } else {
852
            this.setLangName(language);
853
        }
854
        this.setExtension(this.manager.getExtensionOfLanguage(getLangName()));
855

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

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

    
893
    public ScriptingUnit get(File file) {
894
        return this.manager.getScript(file);
895
    }
896

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

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

    
945
    @Override
946
    public List<String> getNames() {
947
        ScriptEngine theEngine = this.getEngine();
948
        if( theEngine instanceof MyPyScriptEngine ) {
949
            return ((MyPyScriptEngine) theEngine).getLocalNames();
950
        }
951
        return null;
952
    }    
953
}