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

History | View | Annotate | Download (32 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
import org.apache.commons.io.Charsets;
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", "UseSpecificCatch"})
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
    public DefaultScriptingScript(ScriptingFolder parent, ScriptingManager manager, String id, String langName) {
145
        this(parent, ScriptingManager.UNIT_SCRIPT, manager, id);
146
        if( !StringUtils.isBlank(langName) ) {
147
            this.setLangName(langName);
148
        }
149
    }
150

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

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

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

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

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

    
225

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

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

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

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

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

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

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

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

    
403
    @Override
404
    protected void loadInf(Ini prefs) {
405
        super.loadInf(prefs);
406

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

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

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

    
440
    @Override
441
    public void save() {
442
        this.saveInfo();
443
        // Guardo el codigo en el fichero
444
        File fcode = this.getFileResource(this.getExtension());
445
        try {
446
            String text = this.getCode();
447
            Charset encoding = Charsets.toCharset(EncodingUtils.getEncoding(text));           
448
            FileUtils.write(fcode, text, encoding);
449
        } catch (Exception e) {
450
            LOGGER.warn("Can't write code to file '" + fcode.getAbsolutePath() + "'.", e);
451
        }
452
        this.setSaved(true);
453
    }
454

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

    
488
    }
489

    
490
    @Override
491
    public String getLangName() {
492
        return this.langName;
493
    }
494

    
495
    protected void setLangName(final String langName) {
496
        if( langName == null ) {
497
            return;
498
        }
499
        this.langName = langName;
500
        this.setExtension(this.manager.getExtensionOfLanguage(this.langName));
501
    }
502

    
503
    @Override
504
    public String[] getIconNames() {
505
        return new String[]{
506
            "scripting-icon-" + this.getLangName().toLowerCase(),
507
            "scripting-icon-" + this.getLangName().toLowerCase() + "-open"
508
        };
509
    }
510

    
511
    @Override
512
    public String getMainName() {
513
        return this.mainName;
514
    }
515

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

    
528
    public String getExtension() {
529
        return this.extension;
530
    }
531

    
532
    public void setExtension(final String extension) {
533
        if (!extension.startsWith(".")) {
534
            this.extension = "." + extension;
535
        } else {
536
            this.extension = extension;
537
        }
538
    }
539

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

    
545
    @Override
546
    public void deleteObserver(final Observer o) {
547
        this.delegatedObservable.deleteObserver(o);
548
    }
549

    
550
    @Override
551
    public void deleteObservers() {
552
        this.delegatedObservable.deleteObservers();
553
    }
554

    
555
    @Override
556
    public void put(final String name, final Object value) {
557
        this.getEngine().put(name, value);
558
    }
559

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

    
615
    public void addDisposable(Disposable disposable) {
616
        //pass
617
    }
618

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

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

    
638
        
639
    @Override
640
    public Object run() {
641
        return this.run(null);
642
    }
643

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

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

    
690
        } catch (Error | Exception e) {
691
            ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e);
692
            notifyErrors(ee, "invoke");
693
            throw ee;
694
        }
695
    }
696

    
697
    @Override
698
    public File getScriptFile() {
699
        return this.getFileResource(extension);
700
    }
701

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

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

    
762
    @Override
763
    public File getResource(String filename) {
764
        return new File(this.getParent().getFile(), filename);
765
    }
766

    
767
    @Override
768
    public String getMimeType() {
769
        return "text/"+ this.getLangName();
770
    }
771

    
772
    @Override
773
    protected void console_println(String s) {
774
        super.console_println(s);
775
        try {
776
            this.stdout.write(s+"\n");
777
        } catch (IOException ex) {
778
        }
779
    }
780

    
781
    class ScriptTask extends AbstractMonitorableTask {
782

    
783
        ScriptingBaseScript script = null;
784
        Object[] args = null;
785

    
786
        protected ScriptTask(ScriptingBaseScript script, Object[] args) {
787
            super(script.getName(), false);
788
            this.args = args;
789
            this.script = script;
790
            this.script.put("task", this);
791
            this.script.put("taskStatus", this.getTaskStatus());
792
        }
793

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

    
813
        public void showTaskStatus() {
814
            this.taskStatus.add();
815
        }
816
    }
817

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

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

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

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

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

    
897
    public ScriptingUnit get(File file) {
898
        return this.manager.getScript(file);
899
    }
900

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

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

    
949
    @Override
950
    public List<String> getNames() {
951
        ScriptEngine theEngine = this.getEngine();
952
        if( theEngine instanceof MyPyScriptEngine ) {
953
            return ((MyPyScriptEngine) theEngine).getLocalNames();
954
        }
955
        return null;
956
    }    
957

    
958
    @Override
959
    public String getText() {
960
        return this.getCode();
961
    }
962

    
963
    @Override
964
    public void setText(String text) {
965
        this.setCode(text);
966
    }
967

    
968
}