Statistics
| Revision:

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

History | View | Annotate | Download (31 KB)

1 164 jobacas
package org.gvsig.scripting.impl;
2
3 1023 jjdelcerro
import groovy.lang.GroovyClassLoader;
4
import groovy.util.GroovyScriptEngine;
5 164 jobacas
import java.io.File;
6
import java.io.IOException;
7 468 jjdelcerro
import java.io.InputStream;
8 679 jjdelcerro
import java.io.PrintStream;
9
import java.io.Writer;
10 1023 jjdelcerro
import java.net.URL;
11 679 jjdelcerro
import java.nio.charset.Charset;
12 702 jjdelcerro
import java.util.ArrayList;
13 1023 jjdelcerro
import java.util.Arrays;
14 679 jjdelcerro
import java.util.HashSet;
15 1023 jjdelcerro
import java.util.LinkedHashSet;
16 301 jjdelcerro
import java.util.List;
17 679 jjdelcerro
import java.util.Set;
18 164 jobacas
19 301 jjdelcerro
import javax.script.Compilable;
20
import javax.script.CompiledScript;
21 164 jobacas
import javax.script.Invocable;
22
import javax.script.ScriptEngine;
23
import javax.script.ScriptException;
24
25 441 jjdelcerro
import org.apache.commons.io.FileUtils;
26
import org.apache.commons.io.FilenameUtils;
27 468 jjdelcerro
import org.apache.commons.io.IOUtils;
28
import org.apache.commons.lang3.StringUtils;
29 650 jjdelcerro
import org.apache.commons.lang3.exception.ExceptionUtils;
30 301 jjdelcerro
import org.gvsig.scripting.CompileErrorException;
31
import org.gvsig.scripting.ExecuteErrorException;
32 595 jjdelcerro
import org.gvsig.scripting.Main;
33 406 jjdelcerro
import org.gvsig.scripting.ScriptingBaseScript;
34 164 jobacas
import org.gvsig.scripting.ScriptingFolder;
35
import org.gvsig.scripting.ScriptingManager;
36
import org.gvsig.scripting.ScriptingScript;
37 453 jjdelcerro
import org.gvsig.scripting.ScriptingUnit;
38 391 vacevedo
import org.gvsig.tools.dispose.Disposable;
39 164 jobacas
import org.gvsig.tools.observer.Observer;
40
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
41 406 jjdelcerro
import org.gvsig.tools.task.AbstractMonitorableTask;
42 301 jjdelcerro
import org.ini4j.Ini;
43 1023 jjdelcerro
import org.python.core.Py;
44 650 jjdelcerro
import org.python.core.PyException;
45
import org.python.core.PyString;
46 1023 jjdelcerro
import org.python.core.PySystemState;
47 650 jjdelcerro
import org.python.core.PyTraceback;
48 1023 jjdelcerro
import org.python.core.imp;
49
import org.python.jsr223.MyPyScriptEngine;
50 441 jjdelcerro
import org.slf4j.Logger;
51
import org.slf4j.LoggerFactory;
52 164 jobacas
53 1063 jjdelcerro
@SuppressWarnings("EqualsAndHashcode")
54 301 jjdelcerro
public class DefaultScriptingScript extends AbstractScript implements
55 468 jjdelcerro
        ScriptingScript {
56 301 jjdelcerro
57 1023 jjdelcerro
    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
58 468 jjdelcerro
    private static final Logger logger = LoggerFactory.getLogger(DefaultScriptingScript.class);
59
    protected String langName;
60
    protected String extension = null;
61 669 jjdelcerro
    protected String librarySuffix = null;
62 468 jjdelcerro
    protected ScriptEngine engine = null;
63
    protected CompiledScript compiledCode;
64 1023 jjdelcerro
    protected boolean useSysPath;
65 301 jjdelcerro
66 468 jjdelcerro
    private String code = null;
67
    private String mainName = "main";
68
    private final DelegateWeakReferencingObservable delegatedObservable;
69 679 jjdelcerro
    private OutputWriter stdout;
70
    private OutputWriter stderr;
71 164 jobacas
72 679 jjdelcerro
    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 1023 jjdelcerro
    @SuppressWarnings("OverridableMethodCallInConstructor")
129 468 jjdelcerro
    protected DefaultScriptingScript(ScriptingFolder parent, String typename, ScriptingManager manager, String id) {
130
        super(parent, typename, manager, id);
131 1023 jjdelcerro
        this.useSysPath = false;
132 468 jjdelcerro
        this.setLangName("python");
133
        this.setSaved(true);
134
        this.delegatedObservable = new DelegateWeakReferencingObservable(this);
135 679 jjdelcerro
        this.stdout = new OutputWriter(System.out);
136
        this.stderr = new OutputWriter(System.err);
137 468 jjdelcerro
    }
138 453 jjdelcerro
139 468 jjdelcerro
    public DefaultScriptingScript(ScriptingFolder parent, ScriptingManager manager, String id) {
140
        this(parent, ScriptingManager.UNIT_SCRIPT, manager, id);
141
    }
142 164 jobacas
143 679 jjdelcerro
    @Override
144 1063 jjdelcerro
    public int hashCode() {
145
        File f = this.getFile();
146
        if( f!=null ) {
147
            return "#$FILE$#".hashCode() + f.getAbsolutePath().hashCode();
148
        }
149
        String s = this.getId();
150
        if( s != null ) {
151
            return "#$ID$#".hashCode() + s.hashCode();
152
        }
153
        s = this.getCode();
154
        if( s != null ) {
155
            return "#$CODE$#".hashCode() + s.hashCode();
156
        }
157
        return super.hashCode();
158
    }
159
    @Override
160 679 jjdelcerro
    public void addStdoutWriter(Writer out) {
161
        this.stdout.addWriter(out);
162
    }
163
164
    @Override
165
    public void addStderrWriter(Writer err) {
166
        this.stderr.addWriter(err);
167
    }
168
169
    @Override
170
    public void removeStdoutWriter(Writer out) {
171
        this.stdout.removeWriter(out);
172
    }
173
174
    @Override
175
    public void removeStderrWriter(Writer err) {
176
        this.stdout.removeWriter(err);
177
    }
178
179 468 jjdelcerro
    public Object __getattr__(String name) {
180 764 jjdelcerro
        try {
181 1023 jjdelcerro
            ScriptEngine theEngine = this.getEngine();
182 764 jjdelcerro
            this.compile();
183 1023 jjdelcerro
            return theEngine.get(name);
184 764 jjdelcerro
        } catch(Exception ex) {
185
            return null;
186
        }
187 468 jjdelcerro
    }
188 301 jjdelcerro
189 468 jjdelcerro
    public void __setattr__(String name, Object value) {
190 1023 jjdelcerro
        ScriptEngine theEngine = this.getEngine();
191 468 jjdelcerro
        this.compile();
192 1023 jjdelcerro
        theEngine.put(name, value);
193 468 jjdelcerro
    }
194 301 jjdelcerro
195 468 jjdelcerro
    public Object __call__() {
196
        return this.run();
197
    }
198 301 jjdelcerro
199 468 jjdelcerro
    public Object __call__(Object[] args) {
200
        return this.run(args);
201
    }
202 441 jjdelcerro
203 679 jjdelcerro
    public OutputWriter getStdout() {
204
        return this.stdout;
205
    }
206
207
    public OutputWriter getStderr() {
208
        return this.stderr;
209
    }
210
211 468 jjdelcerro
    protected void notifyErrors(Exception exception, String command) {
212
        this.delegatedObservable.notifyObservers(new BaseScriptingNotifycation(
213
                this, BaseScriptingNotifycation.RUNTIME_ERROR_NOTIFICATION,
214
                command, exception));
215
    }
216 164 jobacas
217 301 jjdelcerro
218 468 jjdelcerro
    @Override
219
    public String getCode() {
220
        if (this.code == null) {
221
            File f = null;
222
            try {
223
                f = this.getFileResource(this.extension);
224
                this.code = FileUtils.readFileToString(f);
225
            } catch (IOException e) {
226
                String fname = (f == null) ? "(null)" : f.getAbsolutePath();
227
                logger.warn("Can't load code from file '" + fname + "'.");
228
            }
229
        }
230
        return this.code;
231
    }
232 301 jjdelcerro
233 468 jjdelcerro
    @Override
234
    public void setCode(String code) {
235
        this.code = code;
236
        this.engine = null;
237
        this.compiledCode = null;
238
        this.setSaved(false);
239
    }
240 669 jjdelcerro
241
    @Override
242
    public String getLibrarySuffix() {
243
        return this.librarySuffix;
244
    }
245 301 jjdelcerro
246 669 jjdelcerro
    @Override
247
    public void setLibrarySuffix(String librarySuffix) {
248
        this.librarySuffix = librarySuffix;
249
    }
250
251
    public List<File> getLibFolders() {
252
        List<File> folders = this.manager.getLibFolders();
253
        String suffix = this.getLibrarySuffix();
254
        if( suffix == null ) {
255
            return folders;
256
        }
257
        for( int i=0; i<folders.size(); i++) {
258
            File folder = folders.get(i);
259
            File f = new File(folder.getParentFile(),folder.getName()+suffix);
260
            if( f.exists() ) {
261
                folders.set(i, f);
262
            }
263
        }
264
        return folders;
265
    }
266
267 468 jjdelcerro
    protected String getCodeToInitializeEngine() {
268 1023 jjdelcerro
        String initName = "org/gvsig/scripting/langs/"+this.getLangName().toLowerCase()+"/init.txt";
269 468 jjdelcerro
        try {
270 1023 jjdelcerro
            InputStream template = this.getClass().getClassLoader().getResourceAsStream(initName);
271 468 jjdelcerro
            if( template == null ) {
272
                return null;
273
            }
274
            List<String> lines = IOUtils.readLines(template);
275
            return StringUtils.join(lines, "\n");
276
        } catch (Exception ex) {
277 1023 jjdelcerro
            logger.warn("Can't load code to initialize the script from '"+initName+".",ex);
278 468 jjdelcerro
            return null;
279
        }
280
    }
281 301 jjdelcerro
282 1023 jjdelcerro
    private List<URL> getClassPath() {
283
        Set<URL> classPath = new HashSet<>();
284
285
        try {
286
//            ClassLoader myloader = this.getClass().getClassLoader();
287
//            if( myloader instanceof URLClassLoader) {
288
//                classPath.addAll(Arrays.asList(((URLClassLoader) myloader).getURLs()));
289
//            }
290
            URL url;
291
            if( StringUtils.isEmpty(this.getIsolationGroup()) ) {
292
                url = this.getFile().getParentFile().getCanonicalFile().toURI().toURL();
293
                classPath.add(url);
294
            }
295
296
            url = this.getManager().getUserFolder().getFile().getCanonicalFile().toURI().toURL();
297
            classPath.add(url);
298
299
            List<File> folders = getLibFolders();
300
            if( folders!=null ) {
301
                for (File folder : folders) {
302
                    url = folder.getAbsoluteFile().toURI().toURL();
303
                    classPath.add(url);
304
                }
305
            }
306
307
            ScriptingFolder folders2 = getManager().getSystemFolder();
308
            if( folders2!=null ) {
309
                for (ScriptingUnit folder : folders2.getUnits()) {
310
                    url = folder.getFile().getCanonicalFile().toURI().toURL();
311
                    classPath.add(url);
312
                }
313
            }
314
        } catch(Exception ex) {
315
316
        }
317
        return new ArrayList<>(classPath);
318
    }
319
320
    private void addClassPathToEngine(ScriptEngine engine, List<URL> classPath) {
321
        if( engine instanceof MyPyScriptEngine ) {
322
            PySystemState sys = Py.getSystemState();
323
            if( this.useSysPath ) {
324
//                logger.info("Running without sys.classLoader, "+ this.getFile().getAbsolutePath()+".");
325
//                ReentrantLock importLock =sys.getImportLock();
326
//                importLock.lock();
327
//                try {
328 1037 jjdelcerro
                    Set<PyString> paths = new LinkedHashSet<>();
329 1023 jjdelcerro
                    for( int i=0; i<sys.path.size(); i++) {
330
                        String path = (String) sys.path.get(i);
331 1037 jjdelcerro
                        if( !(path.equals("__pyclasspath__/") || path.equals("__classpath__")) ) {
332
                            paths.add(Py.newString(path));
333
                        }
334 1023 jjdelcerro
                    }
335
                    for (URL url : classPath) {
336
                        String path = FileUtils.toFile(url).getAbsolutePath();
337 1037 jjdelcerro
                        paths.add(Py.newString(path));
338 1023 jjdelcerro
                    }
339 1037 jjdelcerro
                    paths.add(Py.newString("__classpath__"));
340
                    paths.add(Py.newString("__pyclasspath__/"));
341
                    sys.path.addAll(paths);
342
343 1023 jjdelcerro
//                } finally {
344
//                    importLock.unlock();
345
//                }
346
            } else {
347
                ClassLoader loader = sys.getClassLoader();
348
                if( loader == null ) {
349
                    ClassLoader parentClassLoader = imp.getParentClassLoader();
350
                    loader = new MutableURLClassLoader(classPath, parentClassLoader);
351
                    sys.setClassLoader(loader);
352
                } else if( loader instanceof MutableURLClassLoader ) {
353
                    ((MutableURLClassLoader) loader).addUrls(classPath);
354
                }
355
            }
356
357
        } else if( engine instanceof GroovyScriptEngine ) {
358
            GroovyClassLoader loader = ((GroovyScriptEngine)engine).getGroovyClassLoader();
359
            Set<URL> urls = new HashSet();
360
            urls.addAll(Arrays.asList(loader.getURLs()));
361
            for (URL url : classPath) {
362
                if( ! urls.contains(url) ) {
363
                    loader.addURL(url);
364
                }
365
            }
366
        }
367
    }
368
369 499 jjdelcerro
    public ScriptEngine getEngine() {
370 468 jjdelcerro
        if (this.engine == null) {
371 815 jjdelcerro
            synchronized(this.getManager()) {
372
                ScriptEngine scriptEngine = this.manager.getEngineByLanguage(langName, this.getIsolationGroup());
373 1023 jjdelcerro
                addClassPathToEngine(scriptEngine,getClassPath());
374
375 815 jjdelcerro
                scriptEngine.put("script", this);
376
                scriptEngine.put("Main", Main.class);
377
                scriptEngine.getContext().setWriter(stdout);
378
                scriptEngine.getContext().setErrorWriter(stderr);
379
380
                this.engine = scriptEngine;
381 1023 jjdelcerro
                String theCode = this.getCodeToInitializeEngine();
382
                if (theCode != null) {
383 815 jjdelcerro
                    try {
384 1023 jjdelcerro
                        this.engine.eval(theCode);
385 815 jjdelcerro
                    } catch (Exception ex) {
386 1023 jjdelcerro
                        logger.warn("Can't initialize engine with the code:\n" + theCode, ex);
387 815 jjdelcerro
                    }
388 468 jjdelcerro
                }
389
            }
390
        }
391
        return this.engine;
392
    }
393 164 jobacas
394 468 jjdelcerro
    @Override
395
    protected void loadInf(Ini prefs) {
396
        super.loadInf(prefs);
397 164 jobacas
398 468 jjdelcerro
        this.setMainName((String) getInfValue(prefs, "Script", "main", "main"));
399 669 jjdelcerro
        this.setLangName((String) getInfValue(prefs, "Script", "Lang",this.getLangName()));
400
        this.setLibrarySuffix((String) getInfValue(prefs, "Script", "LibraryVersion",null));
401 1023 jjdelcerro
        this.useSysPath = getInfBoolean(prefs, "Script", "useSysPath", false);
402 468 jjdelcerro
    }
403 164 jobacas
404 468 jjdelcerro
    @Override
405
    public void load(ScriptingFolder folder, String id) {
406
        this.setId(id);
407
        this.setParent(folder);
408 164 jobacas
409 1023 jjdelcerro
        String theExtension = FilenameUtils.getExtension(id);
410
        if( theExtension != null ) {
411
            String language = this.manager.getLanguageOfExtension(theExtension);
412 468 jjdelcerro
            if( language != null ) {
413
                this.setLangName(language);
414
            }
415 1023 jjdelcerro
            this.setExtension(theExtension);
416 468 jjdelcerro
        }
417
        File f = getFileResource(".inf");
418
        if (f.isFile()) {
419
            Ini prefs = null;
420
            try {
421
                prefs = new Ini(f);
422
            } catch (Exception e) {
423
                logger.warn("Can't load 'inf' file '" + f.getAbsolutePath() + "'.", e);
424
            }
425
            loadInf(prefs);
426
        }
427 981 jjdelcerro
        this.setCode(null);
428 468 jjdelcerro
        this.setSaved(true);
429
    }
430 301 jjdelcerro
431 468 jjdelcerro
    @Override
432
    public void save() {
433 590 jjdelcerro
        this.saveInfo();
434
        // Guardo el codigo en el fichero
435
        File fcode = this.getFileResource(this.getExtension());
436
        try {
437
            FileUtils.write(fcode, this.getCode());
438
        } catch (Exception e) {
439
            logger.warn("Can't write code to file '" + fcode.getAbsolutePath() + "'.", e);
440
        }
441
        this.setSaved(true);
442
    }
443
444
    private void saveInfo() {
445 468 jjdelcerro
        File f = getFileResource(".inf");
446
        if (!f.isFile()) {
447
            try {
448
                f.createNewFile();
449
            } catch (Exception e) {
450
                logger.warn("Can't create 'inf' file '" + f.getAbsolutePath() + "'.", e);
451
            }
452
        }
453
        Ini prefs = null;
454
        try {
455
            prefs = new Ini(f);
456
        } catch (Exception e) {
457
            logger.warn("Can't load 'inf' file '" + f.getAbsolutePath() + "'.", e);
458
        }
459
        save(prefs);
460
    }
461 590 jjdelcerro
462 468 jjdelcerro
    @Override
463
    protected void save(Ini prefs) {
464
        super.save(prefs);
465
        prefs.put("Script", "main", this.getMainName());
466
        prefs.put("Script", "Lang", this.getLangName());
467 1023 jjdelcerro
        if( this.useSysPath ) {
468
            prefs.put("Script", "useSysPath", this.useSysPath);
469
        }
470 468 jjdelcerro
        try {
471
            prefs.store();
472
        } catch (IOException e) {
473
            String fname = (prefs.getFile() == null) ? "(null)" : prefs.getFile().getAbsolutePath();
474
            logger.warn("Can't save inf file (" + fname + ").", e);
475
        }
476 164 jobacas
477 468 jjdelcerro
    }
478 164 jobacas
479 468 jjdelcerro
    @Override
480
    public String getLangName() {
481
        return this.langName;
482
    }
483 301 jjdelcerro
484 468 jjdelcerro
    protected void setLangName(final String langName) {
485
        if( langName == null ) {
486
            return;
487
        }
488
        this.langName = langName;
489
        this.setExtension(this.manager.getExtensionOfLanguage(this.langName));
490
    }
491 164 jobacas
492 468 jjdelcerro
    @Override
493
    public String[] getIconNames() {
494
        return new String[]{
495 1066 jjdelcerro
            "scripting-icon-" + this.getLangName().toLowerCase(),
496
            "scripting-icon-" + this.getLangName().toLowerCase() + "-open"
497 468 jjdelcerro
        };
498
    }
499 164 jobacas
500 468 jjdelcerro
    @Override
501
    public String getMainName() {
502
        return this.mainName;
503
    }
504 301 jjdelcerro
505 468 jjdelcerro
    @Override
506
    public void setMainName(final String mainName) {
507
        this.mainName = mainName;
508
    }
509 702 jjdelcerro
510
    @Override
511
    public List<File> getFiles() {
512
        List<File> l = new ArrayList<>();
513
        l.add(this.getScriptFile());
514
        return l;
515
    }
516 301 jjdelcerro
517 468 jjdelcerro
    public String getExtension() {
518
        return this.extension;
519
    }
520 164 jobacas
521 468 jjdelcerro
    public void setExtension(final String extension) {
522
        if (!extension.startsWith(".")) {
523
            this.extension = "." + extension;
524
        } else {
525
            this.extension = extension;
526
        }
527
    }
528 164 jobacas
529 468 jjdelcerro
    @Override
530
    public void addObserver(final Observer o) {
531
        this.delegatedObservable.addObserver(o);
532
    }
533 301 jjdelcerro
534 468 jjdelcerro
    @Override
535
    public void deleteObserver(final Observer o) {
536
        this.delegatedObservable.deleteObserver(o);
537
    }
538 301 jjdelcerro
539 468 jjdelcerro
    @Override
540
    public void deleteObservers() {
541
        this.delegatedObservable.deleteObservers();
542
    }
543 301 jjdelcerro
544 468 jjdelcerro
    @Override
545
    public void put(final String name, final Object value) {
546
        this.getEngine().put(name, value);
547
    }
548 301 jjdelcerro
549 468 jjdelcerro
    @Override
550
    public void compile() {
551
        if (this.compiledCode == null) {
552 1023 jjdelcerro
            ScriptEngine theEngine = this.getEngine();
553
            if (theEngine instanceof Compilable) {
554 468 jjdelcerro
                try {
555 1023 jjdelcerro
                    Compilable compilable = (Compilable) theEngine;
556
                    String theCode = this.getCode();
557 630 jjdelcerro
                    if( "python".equalsIgnoreCase(this.getLangName()) ) {
558
                        // If a Unicode string with a coding declaration is passed to compile(),
559
                        // a SyntaxError will be raised, but this is necessary to import from
560
                        // another module, so we remove it.
561
                        // http://bugs.jython.org/issue1696
562 1023 jjdelcerro
                        theCode = theCode.replaceFirst("^\\s*#([^:\\n]*)coding:","#$1 c-o-d-i-n-g:");
563 630 jjdelcerro
                    }
564 1023 jjdelcerro
                    this.compiledCode = compilable.compile(theCode);
565
                    if( theEngine instanceof Invocable) {
566 595 jjdelcerro
                        this.compiledCode.eval();
567
                    }
568 468 jjdelcerro
                } catch (ScriptException e) {
569 650 jjdelcerro
                    Object[] location = this.getLocation(e);
570
                    CompileErrorException ce = new CompileErrorException(
571
                            e.getMessage(),
572
                            (File) location[0],
573
                            (int) location[1],
574
                            (int) location[2],
575
                            e
576
                    );
577 468 jjdelcerro
                    notifyErrors(ce, "compile");
578
                    throw ce;
579
                } catch (Throwable e) {
580 650 jjdelcerro
                    CompileErrorException ce = new CompileErrorException(e.getMessage(), this.getScriptFile(), e);
581 468 jjdelcerro
                    notifyErrors(new Exception(e), "compile");
582
                    throw ce;
583
                }
584
            } else {
585 1023 jjdelcerro
                String theCode = this.getCode();
586 468 jjdelcerro
                try {
587 1023 jjdelcerro
                    theEngine.eval(theCode);
588 468 jjdelcerro
                } catch (ScriptException e) {
589 650 jjdelcerro
                    Object[] location = this.getLocation(e);
590
                    CompileErrorException ce = new CompileErrorException(
591
                            e.getMessage(),
592
                            (File) location[0],
593
                            (int) location[1],
594
                            (int) location[2],
595
                            e
596
                    );
597 468 jjdelcerro
                    notifyErrors(ce, "compile");
598
                    throw ce;
599
                }
600
            }
601
        }
602
    }
603 301 jjdelcerro
604 560 jjdelcerro
    public void addDisposable(Disposable disposable) {
605
        //pass
606
    }
607
608
    /**
609
     * Run the main function of this script.
610
     * This method is created by familiarity when running the script from another script.
611
     * @return
612
     */
613
    public Object main() {
614 468 jjdelcerro
        return this.run(null);
615
    }
616 301 jjdelcerro
617 560 jjdelcerro
    /**
618
     * Run the main function of this script.
619
     * This method is created by familiarity when running the script from another script.
620 679 jjdelcerro
     * @param args
621 560 jjdelcerro
     * @return
622
     */
623
    public Object main(Object... args) {
624
        return this.run(args);
625 468 jjdelcerro
    }
626 406 jjdelcerro
627 560 jjdelcerro
628 679 jjdelcerro
    @Override
629 560 jjdelcerro
    public Object run() {
630
        return this.run(null);
631
    }
632
633 468 jjdelcerro
    @Override
634
    public Object run(Object args[]) {
635 630 jjdelcerro
        if( !this.isEnabled() ) {
636
            System.err.printf("The script '"+this.getName()+"' is not enabled, see properties page to change.\n");
637
            return null;
638
        }
639 468 jjdelcerro
        if (args == null) {
640
            args = new Object[]{};
641
        }
642
        this.compile();
643
        return this.invokeFunction(this.getMainName(), args);
644
    }
645 441 jjdelcerro
646 468 jjdelcerro
    @Override
647
    public Object invokeFunction(final String name, Object args[]) {
648 595 jjdelcerro
        try {
649
            if (this.getEngine() instanceof Invocable) {
650
                Invocable invocable = (Invocable) this.getEngine();
651
                this.compile();
652
                if (args == null) {
653
                    args = new Object[]{};
654
                }
655
                return invocable.invokeFunction(name, args);
656
            } else {
657
                if (this.compiledCode != null) {
658
                    Object x = this.compiledCode.eval();
659
                    if( x instanceof Main ) {
660
                        return ((Main) x).main(args);
661
                    } else if(x instanceof Runnable) {
662
                        ((Runnable) x).run();
663
                    }
664
                }
665
                return null;
666 468 jjdelcerro
            }
667 595 jjdelcerro
        } catch (ScriptException e) {
668 650 jjdelcerro
            Object[] location = this.getLocation(e);
669
            ExecuteErrorException ee = new ExecuteErrorException(
670
                    e.getMessage(),
671
                    (File) location[0],
672
                    (int) location[1],
673
                    (int) location[2],
674
                    e
675
            );
676 595 jjdelcerro
            notifyErrors(ee, "invoke");
677
            throw ee;
678 533 jjdelcerro
679 595 jjdelcerro
        } catch (Error | Exception e) {
680 650 jjdelcerro
            ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e);
681 595 jjdelcerro
            notifyErrors(ee, "invoke");
682
            throw ee;
683 468 jjdelcerro
        }
684
    }
685 415 jldominguez
686 468 jjdelcerro
    @Override
687 650 jjdelcerro
    public File getScriptFile() {
688
        return this.getFileResource(extension);
689
    }
690
691
    private Object[] getLocation(ScriptException e) {
692
        Throwable[] es = ExceptionUtils.getThrowables(e);
693
        Exception dbgex; // Para debug con mas comodidad
694
        for( Throwable t : es) {
695
            if( t instanceof PyException ) {
696
                try {
697
                    PyException pyex = (PyException)t;
698
                    PyTraceback tb = pyex.traceback;
699
                    if( tb!=null ) {
700
                        while( tb.tb_next!=null ) {
701
                            tb = (PyTraceback) tb.tb_next;
702
                        }
703
                        String s = tb.tb_frame.f_globals.__getitem__(new PyString("__file__")).asString();
704
                        if( s.endsWith("$py.class") ) {
705
                            s = s.substring(0, s.length()-9) + ".py";
706
                            File resource = new File(s);
707
                            return new Object[] { resource, tb.tb_lineno, 0};
708
                        }
709
                        return new Object[] { this.getScriptFile(), tb.tb_lineno, 0};
710
                    }
711
                } catch(Exception ex) {
712
                    // Pass
713
                    dbgex = ex;
714
                }
715
            }
716
        }
717
        int column = e.getColumnNumber();
718
        if( column < 0 ) {
719
            column = 0;
720
        }
721
        return new Object[] { this.getScriptFile(), e.getLineNumber(), column };
722
    }
723
724
725
    @Override
726 468 jjdelcerro
    public Object invokeMethod(final Object obj, final String name, Object[] args)
727
            throws NoSuchMethodException {
728 441 jjdelcerro
729 468 jjdelcerro
        if (this.getEngine() instanceof Invocable) {
730
            Invocable invocable = (Invocable) this.getEngine();
731
            this.compile();
732
            if (args == null) {
733
                args = new Object[]{};
734
            }
735
            try {
736
                return invocable.invokeMethod(obj, name, args);
737
            } catch (ScriptException e) {
738 650 jjdelcerro
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e.getLineNumber(), e.getColumnNumber(), e);
739 468 jjdelcerro
                notifyErrors(ee, "invoke");
740
                throw ee;
741
            } catch (Throwable e) {
742 650 jjdelcerro
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e);
743 468 jjdelcerro
                notifyErrors(ee, "invoke");
744
                throw ee;
745
            }
746
        } else {
747
            return null;
748
        }
749
    }
750 441 jjdelcerro
751 468 jjdelcerro
    @Override
752
    public File getResource(String filename) {
753
        return new File(this.getParent().getFile(), filename);
754
    }
755 441 jjdelcerro
756 468 jjdelcerro
    @Override
757
    public String getMimeType() {
758
        return "text/"+ this.getLangName();
759
    }
760 441 jjdelcerro
761 679 jjdelcerro
    @Override
762
    protected void console_println(String s) {
763
        super.console_println(s);
764
        try {
765
            this.stdout.write(s+"\n");
766
        } catch (IOException ex) {
767
        }
768
    }
769
770 468 jjdelcerro
    class ScriptTask extends AbstractMonitorableTask {
771
772
        ScriptingBaseScript script = null;
773
        Object[] args = null;
774
775
        protected ScriptTask(ScriptingBaseScript script, Object[] args) {
776
            super(script.getName(), false);
777
            this.args = args;
778
            this.script = script;
779
            this.script.put("task", this);
780
            this.script.put("taskStatus", this.getTaskStatus());
781
        }
782
783
        @Override
784
        public void run() {
785
            try {
786
                console_println("Running script " + this.script.getName() + ".");
787
                script.run(this.args);
788
                console_println("Script " + this.script.getName() + " terminated.");
789
            } catch (Throwable e) {
790
                console_println("Stript " + this.script.getName() + " aborted.");
791
            } finally {
792
                this.taskStatus.terminate();
793
                try {
794
                    Thread.sleep(3000);
795
                } catch (InterruptedException e) {
796
                    // Ignore
797
                }
798
                this.taskStatus.remove();
799
            }
800
        }
801
802
        public void showTaskStatus() {
803
            this.taskStatus.add();
804
        }
805
    }
806
807
    @Override
808
    public void runAsTask(Object[] args) {
809 630 jjdelcerro
        if( !this.isEnabled() ) {
810
            System.err.printf("The script '"+this.getName()+"' is not enabled, see properties page to change.\n");
811
            return;
812
        }
813 468 jjdelcerro
        ScriptTask task = new ScriptTask(this, args);
814
        task.start();
815
    }
816
817
    @Override
818
    public boolean remove() {
819
        boolean r = true;
820
        File folder = this.getParent().getFile();
821
        File f = new File(folder, this.getId() + ".inf");
822
        try {
823
            FileUtils.forceDelete(f);
824
        } catch (IOException e) {
825
            logger.warn("Can't remove inf file '" + f.getAbsolutePath() + "'.", e);
826
            r = false;
827
        }
828
        try {
829
            f = new File(folder, this.getId() + this.getExtension());
830
            FileUtils.forceDelete(f);
831
        } catch (IOException e) {
832
            logger.warn("Can't remove code file '" + f.getAbsolutePath() + "'.", e);
833
            r = false;
834
        }
835
        return r;
836
    }
837
838
    @Override
839
    public void create(ScriptingFolder folder, String id, String language) {
840
        this.setParent(folder);
841
        this.setId(id);
842
        if (language == null) {
843
            this.setLangName("python");
844
        } else {
845
            this.setLangName(language);
846
        }
847
        this.setExtension(this.manager.getExtensionOfLanguage(getLangName()));
848
849
        File file = new File(folder.getFile(), id + ".inf");
850
        try {
851
            file.createNewFile();
852
        } catch (IOException e) {
853
            logger.warn("Can't create file of the dialog in '" + file.getAbsolutePath() + "'.", e);
854
        }
855 724 jjdelcerro
        File fcode = this.getFileResource(this.getExtension());
856
        if( fcode.exists() ) {
857
            this.saveInfo();
858
        } else {
859
            String template = this.getNewTemplate();
860
            if( template != null ) {
861
                this.setCode(template);
862
            }
863
            this.save();
864 468 jjdelcerro
        }
865
    }
866
867
    public String getNewTemplate() {
868 1023 jjdelcerro
        String theTemplateName = "org/gvsig/scripting/langs/"+this.getLangName().toLowerCase()+"/new_template.txt";
869 468 jjdelcerro
        try {
870 1023 jjdelcerro
            InputStream template = this.getClass().getClassLoader().getResourceAsStream(theTemplateName);
871 468 jjdelcerro
            if( template == null ) {
872
                return null;
873
            }
874
            List<String> lines = IOUtils.readLines(template);
875
            return StringUtils.join(lines, "\n");
876
        } catch (Exception ex) {
877 1023 jjdelcerro
            logger.warn("Can't load new-template from '"+theTemplateName+"'.",ex);
878 468 jjdelcerro
            return null;
879
        }
880
    }
881
882
    public ScriptingUnit get(String name) {
883
        return this.manager.getScript(name);
884
    }
885 560 jjdelcerro
886
    public ScriptingUnit get(File file) {
887
        return this.manager.getScript(file);
888
    }
889 590 jjdelcerro
890
    @Override
891
    public boolean move(ScriptingFolder target) {
892
        if (! manager.validateUnitId(target, this.getId()) ) {
893
            logger.info("Can't move script '"+this.getId()+"' to '"+target.getFile().getAbsolutePath()+"', is not valid.");
894
            return false;
895
        }
896
        if( !this.isSaved() ) {
897
            logger.info("Can't move script '"+this.getId()+"', is not saved.");
898
            return false;
899
        }
900
        try {
901
            File codefile = this.getFileResource(this.extension);
902
            FileUtils.moveFileToDirectory(this.getFile(), target.getFile(),true);
903
            FileUtils.moveFileToDirectory(codefile, target.getFile(), true);
904
            this.parent = target;
905
            this.load(target, id);
906
        } catch (IOException ex) {
907
            logger.info("Can't move script '"+this.getId()+"' to '"+target.getFile().getAbsolutePath()+"', "+ex.getMessage(),ex);
908
            return false;
909
        }
910
        return true;
911
    }
912
913
    @Override
914
    public boolean rename(String newId) {
915
        if (! manager.validateUnitId(this.getParent(), newId) ) {
916
            logger.info("Can't rename script '"+this.getId()+"', target id '"+newId+"' is not valid.");
917
            return false;
918
        }
919
        if( !this.isSaved() ) {
920
            logger.info("Can't rename script '"+this.getId()+"', is not saved.");
921
            return false;
922
        }
923
        try {
924
            ScriptingFolder target = this.getParent();
925
            File codefile = this.getFileResource(this.extension);
926
            FileUtils.moveFile(this.getFile(),  new File(target.getFile(),newId+".inf") );
927
            FileUtils.moveFile(codefile, new File(target.getFile(),newId+this.extension));
928
            this.setId(newId);
929
            this.saveInfo();
930
            this.load(target, id);
931
        } catch (IOException ex) {
932
            logger.info("Can't rename script '"+this.getId()+"' to '"+newId+"', "+ex.getMessage(),ex);
933
            return false;
934
        }
935
        return true;
936
    }
937
938 468 jjdelcerro
}