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 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 1215 jjdelcerro
import org.apache.commons.io.Charsets;
25 164 jobacas
26 441 jjdelcerro
import org.apache.commons.io.FileUtils;
27
import org.apache.commons.io.FilenameUtils;
28 468 jjdelcerro
import org.apache.commons.io.IOUtils;
29
import org.apache.commons.lang3.StringUtils;
30 650 jjdelcerro
import org.apache.commons.lang3.exception.ExceptionUtils;
31 301 jjdelcerro
import org.gvsig.scripting.CompileErrorException;
32
import org.gvsig.scripting.ExecuteErrorException;
33 595 jjdelcerro
import org.gvsig.scripting.Main;
34 406 jjdelcerro
import org.gvsig.scripting.ScriptingBaseScript;
35 164 jobacas
import org.gvsig.scripting.ScriptingFolder;
36
import org.gvsig.scripting.ScriptingManager;
37
import org.gvsig.scripting.ScriptingScript;
38 453 jjdelcerro
import org.gvsig.scripting.ScriptingUnit;
39 391 vacevedo
import org.gvsig.tools.dispose.Disposable;
40 164 jobacas
import org.gvsig.tools.observer.Observer;
41
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
42 406 jjdelcerro
import org.gvsig.tools.task.AbstractMonitorableTask;
43 301 jjdelcerro
import org.ini4j.Ini;
44 1023 jjdelcerro
import org.python.core.Py;
45 650 jjdelcerro
import org.python.core.PyException;
46
import org.python.core.PyString;
47 1023 jjdelcerro
import org.python.core.PySystemState;
48 650 jjdelcerro
import org.python.core.PyTraceback;
49 1023 jjdelcerro
import org.python.core.imp;
50
import org.python.jsr223.MyPyScriptEngine;
51 441 jjdelcerro
import org.slf4j.Logger;
52
import org.slf4j.LoggerFactory;
53 164 jobacas
54 1221 jjdelcerro
@SuppressWarnings({"EqualsAndHashcode", "UseSpecificCatch"})
55 301 jjdelcerro
public class DefaultScriptingScript extends AbstractScript implements
56 468 jjdelcerro
        ScriptingScript {
57 301 jjdelcerro
58 1023 jjdelcerro
    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
59 1221 jjdelcerro
    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultScriptingScript.class);
60 468 jjdelcerro
    protected String langName;
61
    protected String extension = null;
62 669 jjdelcerro
    protected String librarySuffix = null;
63 468 jjdelcerro
    protected ScriptEngine engine = null;
64
    protected CompiledScript compiledCode;
65 1023 jjdelcerro
    protected boolean useSysPath;
66 301 jjdelcerro
67 468 jjdelcerro
    private String code = null;
68
    private String mainName = "main";
69
    private final DelegateWeakReferencingObservable delegatedObservable;
70 679 jjdelcerro
    private OutputWriter stdout;
71
    private OutputWriter stderr;
72 164 jobacas
73 679 jjdelcerro
    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 1221 jjdelcerro
                LOGGER.warn("Can't output",ex);
89 679 jjdelcerro
            }
90
            for (Writer writer : writers) {
91
                try {
92
                    writer.write(cbuf, off, len);
93
                } catch (Exception ex) {
94 1221 jjdelcerro
                    LOGGER.warn("Can't output",ex);
95 679 jjdelcerro
                }
96
            }
97
        }
98
99
        @Override
100
        public void flush() throws IOException {
101
            try {
102
                out.flush();
103
            } catch (Exception ex) {
104 1221 jjdelcerro
                LOGGER.warn("Can't flush",ex);
105 679 jjdelcerro
            }
106
            for (Writer writer : writers) {
107
                try {
108
                    writer.flush();
109
                } catch (Exception ex) {
110 1221 jjdelcerro
                    LOGGER.warn("Can't flush",ex);
111 679 jjdelcerro
                }
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 1023 jjdelcerro
    @SuppressWarnings("OverridableMethodCallInConstructor")
130 468 jjdelcerro
    protected DefaultScriptingScript(ScriptingFolder parent, String typename, ScriptingManager manager, String id) {
131
        super(parent, typename, manager, id);
132 1023 jjdelcerro
        this.useSysPath = false;
133 468 jjdelcerro
        this.setLangName("python");
134
        this.setSaved(true);
135
        this.delegatedObservable = new DelegateWeakReferencingObservable(this);
136 679 jjdelcerro
        this.stdout = new OutputWriter(System.out);
137
        this.stderr = new OutputWriter(System.err);
138 468 jjdelcerro
    }
139 453 jjdelcerro
140 468 jjdelcerro
    public DefaultScriptingScript(ScriptingFolder parent, ScriptingManager manager, String id) {
141
        this(parent, ScriptingManager.UNIT_SCRIPT, manager, id);
142
    }
143 164 jobacas
144 1120 jjdelcerro
    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 679 jjdelcerro
    @Override
152 1063 jjdelcerro
    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 679 jjdelcerro
    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 468 jjdelcerro
    public Object __getattr__(String name) {
188 764 jjdelcerro
        try {
189 1023 jjdelcerro
            ScriptEngine theEngine = this.getEngine();
190 764 jjdelcerro
            this.compile();
191 1023 jjdelcerro
            return theEngine.get(name);
192 764 jjdelcerro
        } catch(Exception ex) {
193
            return null;
194
        }
195 468 jjdelcerro
    }
196 301 jjdelcerro
197 468 jjdelcerro
    public void __setattr__(String name, Object value) {
198 1023 jjdelcerro
        ScriptEngine theEngine = this.getEngine();
199 468 jjdelcerro
        this.compile();
200 1023 jjdelcerro
        theEngine.put(name, value);
201 468 jjdelcerro
    }
202 301 jjdelcerro
203 468 jjdelcerro
    public Object __call__() {
204
        return this.run();
205
    }
206 301 jjdelcerro
207 468 jjdelcerro
    public Object __call__(Object[] args) {
208
        return this.run(args);
209
    }
210 441 jjdelcerro
211 679 jjdelcerro
    public OutputWriter getStdout() {
212
        return this.stdout;
213
    }
214
215
    public OutputWriter getStderr() {
216
        return this.stderr;
217
    }
218
219 468 jjdelcerro
    protected void notifyErrors(Exception exception, String command) {
220
        this.delegatedObservable.notifyObservers(new BaseScriptingNotifycation(
221
                this, BaseScriptingNotifycation.RUNTIME_ERROR_NOTIFICATION,
222
                command, exception));
223
    }
224 164 jobacas
225 301 jjdelcerro
226 468 jjdelcerro
    @Override
227
    public String getCode() {
228
        if (this.code == null) {
229
            File f = null;
230
            try {
231
                f = this.getFileResource(this.extension);
232 1215 jjdelcerro
                Charset encoding = Charsets.toCharset(EncodingUtils.getEncoding(f));
233
                this.code = FileUtils.readFileToString(f, encoding);
234 468 jjdelcerro
            } catch (IOException e) {
235
                String fname = (f == null) ? "(null)" : f.getAbsolutePath();
236 1221 jjdelcerro
                LOGGER.warn("Can't load code from file '" + fname + "'.");
237 468 jjdelcerro
            }
238
        }
239
        return this.code;
240
    }
241 301 jjdelcerro
242 468 jjdelcerro
    @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 669 jjdelcerro
250
    @Override
251
    public String getLibrarySuffix() {
252
        return this.librarySuffix;
253
    }
254 301 jjdelcerro
255 669 jjdelcerro
    @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 468 jjdelcerro
    protected String getCodeToInitializeEngine() {
277 1023 jjdelcerro
        String initName = "org/gvsig/scripting/langs/"+this.getLangName().toLowerCase()+"/init.txt";
278 468 jjdelcerro
        try {
279 1023 jjdelcerro
            InputStream template = this.getClass().getClassLoader().getResourceAsStream(initName);
280 468 jjdelcerro
            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 1221 jjdelcerro
            LOGGER.warn("Can't load code to initialize the script from '"+initName+".",ex);
287 468 jjdelcerro
            return null;
288
        }
289
    }
290 301 jjdelcerro
291 1023 jjdelcerro
    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 1037 jjdelcerro
                    Set<PyString> paths = new LinkedHashSet<>();
338 1023 jjdelcerro
                    for( int i=0; i<sys.path.size(); i++) {
339
                        String path = (String) sys.path.get(i);
340 1037 jjdelcerro
                        if( !(path.equals("__pyclasspath__/") || path.equals("__classpath__")) ) {
341
                            paths.add(Py.newString(path));
342
                        }
343 1023 jjdelcerro
                    }
344
                    for (URL url : classPath) {
345
                        String path = FileUtils.toFile(url).getAbsolutePath();
346 1037 jjdelcerro
                        paths.add(Py.newString(path));
347 1023 jjdelcerro
                    }
348 1037 jjdelcerro
                    paths.add(Py.newString("__classpath__"));
349
                    paths.add(Py.newString("__pyclasspath__/"));
350
                    sys.path.addAll(paths);
351
352 1023 jjdelcerro
//                } 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 499 jjdelcerro
    public ScriptEngine getEngine() {
379 468 jjdelcerro
        if (this.engine == null) {
380 815 jjdelcerro
            synchronized(this.getManager()) {
381
                ScriptEngine scriptEngine = this.manager.getEngineByLanguage(langName, this.getIsolationGroup());
382 1023 jjdelcerro
                addClassPathToEngine(scriptEngine,getClassPath());
383
384 815 jjdelcerro
                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 1023 jjdelcerro
                String theCode = this.getCodeToInitializeEngine();
391
                if (theCode != null) {
392 815 jjdelcerro
                    try {
393 1023 jjdelcerro
                        this.engine.eval(theCode);
394 815 jjdelcerro
                    } catch (Exception ex) {
395 1221 jjdelcerro
                        LOGGER.warn("Can't initialize engine with the code:\n" + theCode, ex);
396 815 jjdelcerro
                    }
397 468 jjdelcerro
                }
398
            }
399
        }
400
        return this.engine;
401
    }
402 164 jobacas
403 468 jjdelcerro
    @Override
404
    protected void loadInf(Ini prefs) {
405
        super.loadInf(prefs);
406 164 jobacas
407 468 jjdelcerro
        this.setMainName((String) getInfValue(prefs, "Script", "main", "main"));
408 669 jjdelcerro
        this.setLangName((String) getInfValue(prefs, "Script", "Lang",this.getLangName()));
409
        this.setLibrarySuffix((String) getInfValue(prefs, "Script", "LibraryVersion",null));
410 1023 jjdelcerro
        this.useSysPath = getInfBoolean(prefs, "Script", "useSysPath", false);
411 468 jjdelcerro
    }
412 164 jobacas
413 468 jjdelcerro
    @Override
414
    public void load(ScriptingFolder folder, String id) {
415
        this.setId(id);
416
        this.setParent(folder);
417 164 jobacas
418 1023 jjdelcerro
        String theExtension = FilenameUtils.getExtension(id);
419
        if( theExtension != null ) {
420
            String language = this.manager.getLanguageOfExtension(theExtension);
421 468 jjdelcerro
            if( language != null ) {
422
                this.setLangName(language);
423
            }
424 1023 jjdelcerro
            this.setExtension(theExtension);
425 468 jjdelcerro
        }
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 1221 jjdelcerro
                LOGGER.warn("Can't load 'inf' file '" + f.getAbsolutePath() + "'.", e);
433 468 jjdelcerro
            }
434
            loadInf(prefs);
435
        }
436 981 jjdelcerro
        this.setCode(null);
437 468 jjdelcerro
        this.setSaved(true);
438
    }
439 301 jjdelcerro
440 468 jjdelcerro
    @Override
441
    public void save() {
442 590 jjdelcerro
        this.saveInfo();
443
        // Guardo el codigo en el fichero
444
        File fcode = this.getFileResource(this.getExtension());
445
        try {
446 1221 jjdelcerro
            String text = this.getCode();
447
            Charset encoding = Charsets.toCharset(EncodingUtils.getEncoding(text));
448
            FileUtils.write(fcode, text, encoding);
449 590 jjdelcerro
        } catch (Exception e) {
450 1221 jjdelcerro
            LOGGER.warn("Can't write code to file '" + fcode.getAbsolutePath() + "'.", e);
451 590 jjdelcerro
        }
452
        this.setSaved(true);
453
    }
454
455
    private void saveInfo() {
456 468 jjdelcerro
        File f = getFileResource(".inf");
457
        if (!f.isFile()) {
458
            try {
459
                f.createNewFile();
460
            } catch (Exception e) {
461 1221 jjdelcerro
                LOGGER.warn("Can't create 'inf' file '" + f.getAbsolutePath() + "'.", e);
462 468 jjdelcerro
            }
463
        }
464
        Ini prefs = null;
465
        try {
466
            prefs = new Ini(f);
467
        } catch (Exception e) {
468 1221 jjdelcerro
            LOGGER.warn("Can't load 'inf' file '" + f.getAbsolutePath() + "'.", e);
469 468 jjdelcerro
        }
470
        save(prefs);
471
    }
472 590 jjdelcerro
473 468 jjdelcerro
    @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 1023 jjdelcerro
        if( this.useSysPath ) {
479
            prefs.put("Script", "useSysPath", this.useSysPath);
480
        }
481 468 jjdelcerro
        try {
482
            prefs.store();
483
        } catch (IOException e) {
484
            String fname = (prefs.getFile() == null) ? "(null)" : prefs.getFile().getAbsolutePath();
485 1221 jjdelcerro
            LOGGER.warn("Can't save inf file (" + fname + ").", e);
486 468 jjdelcerro
        }
487 164 jobacas
488 468 jjdelcerro
    }
489 164 jobacas
490 468 jjdelcerro
    @Override
491
    public String getLangName() {
492
        return this.langName;
493
    }
494 301 jjdelcerro
495 468 jjdelcerro
    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 164 jobacas
503 468 jjdelcerro
    @Override
504
    public String[] getIconNames() {
505
        return new String[]{
506 1066 jjdelcerro
            "scripting-icon-" + this.getLangName().toLowerCase(),
507
            "scripting-icon-" + this.getLangName().toLowerCase() + "-open"
508 468 jjdelcerro
        };
509
    }
510 164 jobacas
511 468 jjdelcerro
    @Override
512
    public String getMainName() {
513
        return this.mainName;
514
    }
515 301 jjdelcerro
516 468 jjdelcerro
    @Override
517
    public void setMainName(final String mainName) {
518
        this.mainName = mainName;
519
    }
520 702 jjdelcerro
521
    @Override
522
    public List<File> getFiles() {
523
        List<File> l = new ArrayList<>();
524
        l.add(this.getScriptFile());
525
        return l;
526
    }
527 301 jjdelcerro
528 468 jjdelcerro
    public String getExtension() {
529
        return this.extension;
530
    }
531 164 jobacas
532 468 jjdelcerro
    public void setExtension(final String extension) {
533
        if (!extension.startsWith(".")) {
534
            this.extension = "." + extension;
535
        } else {
536
            this.extension = extension;
537
        }
538
    }
539 164 jobacas
540 468 jjdelcerro
    @Override
541
    public void addObserver(final Observer o) {
542
        this.delegatedObservable.addObserver(o);
543
    }
544 301 jjdelcerro
545 468 jjdelcerro
    @Override
546
    public void deleteObserver(final Observer o) {
547
        this.delegatedObservable.deleteObserver(o);
548
    }
549 301 jjdelcerro
550 468 jjdelcerro
    @Override
551
    public void deleteObservers() {
552
        this.delegatedObservable.deleteObservers();
553
    }
554 301 jjdelcerro
555 468 jjdelcerro
    @Override
556
    public void put(final String name, final Object value) {
557
        this.getEngine().put(name, value);
558
    }
559 301 jjdelcerro
560 468 jjdelcerro
    @Override
561
    public void compile() {
562
        if (this.compiledCode == null) {
563 1023 jjdelcerro
            ScriptEngine theEngine = this.getEngine();
564
            if (theEngine instanceof Compilable) {
565 468 jjdelcerro
                try {
566 1023 jjdelcerro
                    Compilable compilable = (Compilable) theEngine;
567
                    String theCode = this.getCode();
568 630 jjdelcerro
                    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 1023 jjdelcerro
                        theCode = theCode.replaceFirst("^\\s*#([^:\\n]*)coding:","#$1 c-o-d-i-n-g:");
574 630 jjdelcerro
                    }
575 1023 jjdelcerro
                    this.compiledCode = compilable.compile(theCode);
576
                    if( theEngine instanceof Invocable) {
577 595 jjdelcerro
                        this.compiledCode.eval();
578
                    }
579 468 jjdelcerro
                } catch (ScriptException e) {
580 650 jjdelcerro
                    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 468 jjdelcerro
                    notifyErrors(ce, "compile");
589
                    throw ce;
590
                } catch (Throwable e) {
591 650 jjdelcerro
                    CompileErrorException ce = new CompileErrorException(e.getMessage(), this.getScriptFile(), e);
592 468 jjdelcerro
                    notifyErrors(new Exception(e), "compile");
593
                    throw ce;
594
                }
595
            } else {
596 1023 jjdelcerro
                String theCode = this.getCode();
597 468 jjdelcerro
                try {
598 1023 jjdelcerro
                    theEngine.eval(theCode);
599 468 jjdelcerro
                } catch (ScriptException e) {
600 650 jjdelcerro
                    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 468 jjdelcerro
                    notifyErrors(ce, "compile");
609
                    throw ce;
610
                }
611
            }
612
        }
613
    }
614 301 jjdelcerro
615 560 jjdelcerro
    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 468 jjdelcerro
        return this.run(null);
626
    }
627 301 jjdelcerro
628 560 jjdelcerro
    /**
629
     * Run the main function of this script.
630
     * This method is created by familiarity when running the script from another script.
631 679 jjdelcerro
     * @param args
632 560 jjdelcerro
     * @return
633
     */
634
    public Object main(Object... args) {
635
        return this.run(args);
636 468 jjdelcerro
    }
637 406 jjdelcerro
638 560 jjdelcerro
639 679 jjdelcerro
    @Override
640 560 jjdelcerro
    public Object run() {
641
        return this.run(null);
642
    }
643
644 468 jjdelcerro
    @Override
645
    public Object run(Object args[]) {
646 630 jjdelcerro
        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 468 jjdelcerro
        if (args == null) {
651
            args = new Object[]{};
652
        }
653
        this.compile();
654
        return this.invokeFunction(this.getMainName(), args);
655
    }
656 441 jjdelcerro
657 468 jjdelcerro
    @Override
658
    public Object invokeFunction(final String name, Object args[]) {
659 595 jjdelcerro
        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 468 jjdelcerro
            }
678 595 jjdelcerro
        } catch (ScriptException e) {
679 650 jjdelcerro
            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 595 jjdelcerro
            notifyErrors(ee, "invoke");
688
            throw ee;
689 533 jjdelcerro
690 595 jjdelcerro
        } catch (Error | Exception e) {
691 650 jjdelcerro
            ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e);
692 595 jjdelcerro
            notifyErrors(ee, "invoke");
693
            throw ee;
694 468 jjdelcerro
        }
695
    }
696 415 jldominguez
697 468 jjdelcerro
    @Override
698 650 jjdelcerro
    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 468 jjdelcerro
    public Object invokeMethod(final Object obj, final String name, Object[] args)
738
            throws NoSuchMethodException {
739 441 jjdelcerro
740 468 jjdelcerro
        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 650 jjdelcerro
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e.getLineNumber(), e.getColumnNumber(), e);
750 468 jjdelcerro
                notifyErrors(ee, "invoke");
751
                throw ee;
752
            } catch (Throwable e) {
753 650 jjdelcerro
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getScriptFile(), e);
754 468 jjdelcerro
                notifyErrors(ee, "invoke");
755
                throw ee;
756
            }
757
        } else {
758
            return null;
759
        }
760
    }
761 441 jjdelcerro
762 468 jjdelcerro
    @Override
763
    public File getResource(String filename) {
764
        return new File(this.getParent().getFile(), filename);
765
    }
766 441 jjdelcerro
767 468 jjdelcerro
    @Override
768
    public String getMimeType() {
769
        return "text/"+ this.getLangName();
770
    }
771 441 jjdelcerro
772 679 jjdelcerro
    @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 468 jjdelcerro
    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 630 jjdelcerro
        if( !this.isEnabled() ) {
821
            System.err.printf("The script '"+this.getName()+"' is not enabled, see properties page to change.\n");
822
            return;
823
        }
824 468 jjdelcerro
        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 1221 jjdelcerro
            LOGGER.warn("Can't remove inf file '" + f.getAbsolutePath() + "'.", e);
837 468 jjdelcerro
            r = false;
838
        }
839
        try {
840
            f = new File(folder, this.getId() + this.getExtension());
841
            FileUtils.forceDelete(f);
842
        } catch (IOException e) {
843 1221 jjdelcerro
            LOGGER.warn("Can't remove code file '" + f.getAbsolutePath() + "'.", e);
844 468 jjdelcerro
            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 1208 jjdelcerro
        File file = new File(folder.getFile(), this.id + ".inf");
861 468 jjdelcerro
        try {
862
            file.createNewFile();
863
        } catch (IOException e) {
864 1221 jjdelcerro
            LOGGER.warn("Can't create file of the dialog in '" + file.getAbsolutePath() + "'.", e);
865 468 jjdelcerro
        }
866 724 jjdelcerro
        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 468 jjdelcerro
        }
876
    }
877
878
    public String getNewTemplate() {
879 1023 jjdelcerro
        String theTemplateName = "org/gvsig/scripting/langs/"+this.getLangName().toLowerCase()+"/new_template.txt";
880 468 jjdelcerro
        try {
881 1023 jjdelcerro
            InputStream template = this.getClass().getClassLoader().getResourceAsStream(theTemplateName);
882 468 jjdelcerro
            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 1221 jjdelcerro
            LOGGER.warn("Can't load new-template from '"+theTemplateName+"'.",ex);
889 468 jjdelcerro
            return null;
890
        }
891
    }
892
893
    public ScriptingUnit get(String name) {
894
        return this.manager.getScript(name);
895
    }
896 560 jjdelcerro
897
    public ScriptingUnit get(File file) {
898
        return this.manager.getScript(file);
899
    }
900 590 jjdelcerro
901
    @Override
902
    public boolean move(ScriptingFolder target) {
903
        if (! manager.validateUnitId(target, this.getId()) ) {
904 1221 jjdelcerro
            LOGGER.info("Can't move script '"+this.getId()+"' to '"+target.getFile().getAbsolutePath()+"', is not valid.");
905 590 jjdelcerro
            return false;
906
        }
907
        if( !this.isSaved() ) {
908 1221 jjdelcerro
            LOGGER.info("Can't move script '"+this.getId()+"', is not saved.");
909 590 jjdelcerro
            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 1221 jjdelcerro
            LOGGER.info("Can't move script '"+this.getId()+"' to '"+target.getFile().getAbsolutePath()+"', "+ex.getMessage(),ex);
919 590 jjdelcerro
            return false;
920
        }
921
        return true;
922
    }
923
924
    @Override
925
    public boolean rename(String newId) {
926
        if (! manager.validateUnitId(this.getParent(), newId) ) {
927 1221 jjdelcerro
            LOGGER.info("Can't rename script '"+this.getId()+"', target id '"+newId+"' is not valid.");
928 590 jjdelcerro
            return false;
929
        }
930
        if( !this.isSaved() ) {
931 1221 jjdelcerro
            LOGGER.info("Can't rename script '"+this.getId()+"', is not saved.");
932 590 jjdelcerro
            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 1221 jjdelcerro
            LOGGER.info("Can't rename script '"+this.getId()+"' to '"+newId+"', "+ex.getMessage(),ex);
944 590 jjdelcerro
            return false;
945
        }
946
        return true;
947
    }
948
949 1112 jjdelcerro
    @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 1267 jjdelcerro
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 468 jjdelcerro
}