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

History | View | Annotate | Download (18.4 KB)

1
package org.gvsig.scripting.impl;
2

    
3
import java.io.File;
4
import java.io.IOException;
5
import java.io.InputStream;
6
import java.util.List;
7

    
8
import javax.script.Compilable;
9
import javax.script.CompiledScript;
10
import javax.script.Invocable;
11
import javax.script.ScriptEngine;
12
import javax.script.ScriptException;
13

    
14
import org.apache.commons.io.FileUtils;
15
import org.apache.commons.io.FilenameUtils;
16
import org.apache.commons.io.IOUtils;
17
import org.apache.commons.lang3.StringUtils;
18
import org.gvsig.scripting.CompileErrorException;
19
import org.gvsig.scripting.ExecuteErrorException;
20
import org.gvsig.scripting.ScriptingBaseScript;
21
import org.gvsig.scripting.ScriptingFolder;
22
import org.gvsig.scripting.ScriptingManager;
23
import org.gvsig.scripting.ScriptingScript;
24
import org.gvsig.scripting.ScriptingUnit;
25
import org.gvsig.tools.dispose.Disposable;
26
import org.gvsig.tools.observer.Observer;
27
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
28
import org.gvsig.tools.task.AbstractMonitorableTask;
29
import org.ini4j.Ini;
30
import org.slf4j.Logger;
31
import org.slf4j.LoggerFactory;
32

    
33
public class DefaultScriptingScript extends AbstractScript implements
34
        ScriptingScript {
35

    
36
    private static final Logger logger = LoggerFactory.getLogger(DefaultScriptingScript.class);
37
    protected String langName;
38
    protected String extension = null;
39
    protected ScriptEngine engine = null;
40
    protected CompiledScript compiledCode;
41

    
42
    private String code = null;
43
    private String mainName = "main";
44
    private boolean saved;
45
    private final DelegateWeakReferencingObservable delegatedObservable;
46

    
47
    protected DefaultScriptingScript(ScriptingFolder parent, String typename, ScriptingManager manager, String id) {
48
        super(parent, typename, manager, id);
49
        this.setLangName("python");
50
        this.setSaved(true);
51
        this.delegatedObservable = new DelegateWeakReferencingObservable(this);
52
    }
53

    
54
    public DefaultScriptingScript(ScriptingFolder parent, ScriptingManager manager, String id) {
55
        this(parent, ScriptingManager.UNIT_SCRIPT, manager, id);
56
    }
57

    
58
    public Object __getattr__(String name) {
59
        ScriptEngine engine = this.getEngine();
60
        this.compile();
61
        return engine.get(name);
62
    }
63

    
64
    public void __setattr__(String name, Object value) {
65
        ScriptEngine engine = this.getEngine();
66
        this.compile();
67
        engine.put(name, value);
68
    }
69

    
70
    public Object __call__() {
71
        return this.run();
72
    }
73

    
74
    public Object __call__(Object[] args) {
75
        return this.run(args);
76
    }
77

    
78
    protected void notifyErrors(Exception exception, String command) {
79
        this.delegatedObservable.notifyObservers(new BaseScriptingNotifycation(
80
                this, BaseScriptingNotifycation.RUNTIME_ERROR_NOTIFICATION,
81
                command, exception));
82
    }
83

    
84
    @Override
85
    public void setSaved(boolean saved) {
86
        this.saved = saved;
87
    }
88

    
89
    @Override
90
    public String getCode() {
91
        if (this.code == null) {
92
            File f = null;
93
            try {
94
                f = this.getFileResource(this.extension);
95
                this.code = FileUtils.readFileToString(f);
96
            } catch (IOException e) {
97
                String fname = (f == null) ? "(null)" : f.getAbsolutePath();
98
                logger.warn("Can't load code from file '" + fname + "'.");
99
            }
100
        }
101
        return this.code;
102
    }
103

    
104
    @Override
105
    public void setCode(String code) {
106
        this.code = code;
107
        this.engine = null;
108
        this.compiledCode = null;
109
        this.setSaved(false);
110
    }
111

    
112
    protected String getCodeToInitializeEngine() {
113
        String name = "org/gvsig/scripting/langs/"+this.getLangName().toLowerCase()+"/init.txt";
114
        try {
115
            InputStream template = this.getClass().getClassLoader().getResourceAsStream(name);
116
            if( template == null ) {
117
                return null;
118
            }
119
            List<String> lines = IOUtils.readLines(template);
120
            return StringUtils.join(lines, "\n");
121
        } catch (Exception ex) {
122
            logger.warn("Can't load code to initialize the script from '"+name+".",ex);
123
            return null;
124
        }
125
    }
126

    
127
    public ScriptEngine getEngine() {
128
        if (this.engine == null) {
129
            ScriptEngine scriptEngine = this.manager.getEngineByLanguage(langName);
130
            scriptEngine.put("script", this);
131
            this.engine = scriptEngine;
132
            String code = this.getCodeToInitializeEngine();
133
            if (code != null) {
134
                try {
135
                    this.engine.eval(code);
136
                } catch (Exception ex) {
137
                    logger.warn("Can't initialize engine with the code:\n" + code, ex);
138
                }
139
            }
140
        }
141
        return this.engine;
142
    }
143

    
144
    @Override
145
    protected void loadInf(Ini prefs) {
146
        super.loadInf(prefs);
147

    
148
        this.setMainName((String) getInfValue(prefs, "Script", "main", "main"));
149
        this.setLangName((String) getInfValue(prefs, "Script", "Lang",
150
                this.getLangName()));
151
    }
152

    
153
    @Override
154
    public void load(ScriptingFolder folder, String id) {
155
        this.setId(id);
156
        this.setParent(folder);
157

    
158
        String extension = FilenameUtils.getExtension(id);
159
        if( extension != null ) {
160
            String language = this.manager.getLanguageOfExtension(extension);
161
            if( language != null ) {
162
                this.setLangName(language);
163
            }
164
            this.setExtension(extension);
165
        }
166
        File f = getFileResource(".inf");
167
        if (f.isFile()) {
168
            Ini prefs = null;
169
            try {
170
                prefs = new Ini(f);
171
            } catch (Exception e) {
172
                logger.warn("Can't load 'inf' file '" + f.getAbsolutePath() + "'.", e);
173
            }
174
            loadInf(prefs);
175
        }
176
        this.setSaved(true);
177
    }
178

    
179
    @Override
180
    public void save() {
181
        this.saveInfo();
182
        // Guardo el codigo en el fichero
183
        File fcode = this.getFileResource(this.getExtension());
184
        try {
185
            FileUtils.write(fcode, this.getCode());
186
        } catch (Exception e) {
187
            logger.warn("Can't write code to file '" + fcode.getAbsolutePath() + "'.", e);
188
        }
189
        this.setSaved(true);
190
    }
191

    
192
    private void saveInfo() {
193
        File f = getFileResource(".inf");
194
        if (!f.isFile()) {
195
            try {
196
                f.createNewFile();
197
            } catch (Exception e) {
198
                logger.warn("Can't create 'inf' file '" + f.getAbsolutePath() + "'.", e);
199
            }
200
        }
201
        Ini prefs = null;
202
        try {
203
            prefs = new Ini(f);
204
        } catch (Exception e) {
205
            logger.warn("Can't load 'inf' file '" + f.getAbsolutePath() + "'.", e);
206
        }
207
        save(prefs);
208
    }
209
    
210
    @Override
211
    protected void save(Ini prefs) {
212
        super.save(prefs);
213
        prefs.put("Script", "main", this.getMainName());
214
        prefs.put("Script", "Lang", this.getLangName());
215
        try {
216
            prefs.store();
217
        } catch (IOException e) {
218
            String fname = (prefs.getFile() == null) ? "(null)" : prefs.getFile().getAbsolutePath();
219
            logger.warn("Can't save inf file (" + fname + ").", e);
220
        }
221

    
222
    }
223

    
224
    @Override
225
    public String getLangName() {
226
        return this.langName;
227
    }
228

    
229
    protected void setLangName(final String langName) {
230
        if( langName == null ) {
231
            return;
232
        }
233
        this.langName = langName;
234
        this.setExtension(this.manager.getExtensionOfLanguage(this.langName));
235
    }
236

    
237
    @Override
238
    public String[] getIconNames() {
239
        return new String[]{
240
            "scripting_" + this.getLangName().toLowerCase(),
241
            "scripting_" + this.getLangName().toLowerCase() + "_open"
242
        };
243
    }
244

    
245
    @Override
246
    public String getMainName() {
247
        return this.mainName;
248
    }
249

    
250
    @Override
251
    public void setMainName(final String mainName) {
252
        this.mainName = mainName;
253
    }
254

    
255
    public String getExtension() {
256
        return this.extension;
257
    }
258

    
259
    public void setExtension(final String extension) {
260
        if (!extension.startsWith(".")) {
261
            this.extension = "." + extension;
262
        } else {
263
            this.extension = extension;
264
        }
265
    }
266

    
267
    @Override
268
    public boolean isSaved() {
269
        return this.saved;
270
    }
271

    
272
    @Override
273
    public void addObserver(final Observer o) {
274
        this.delegatedObservable.addObserver(o);
275
    }
276

    
277
    @Override
278
    public void deleteObserver(final Observer o) {
279
        this.delegatedObservable.deleteObserver(o);
280
    }
281

    
282
    @Override
283
    public void deleteObservers() {
284
        this.delegatedObservable.deleteObservers();
285
    }
286

    
287
    @Override
288
    public void put(final String name, final Object value) {
289
        this.getEngine().put(name, value);
290
    }
291

    
292
    @Override
293
    public void compile() {
294
        if (this.compiledCode == null) {
295
            ScriptEngine engine = this.getEngine();
296
            if (engine instanceof Compilable) {
297
                try {
298
                    Compilable compilable = (Compilable) engine;
299
                    this.compiledCode = compilable.compile(this.getCode());
300
                    this.compiledCode.eval();
301
                } catch (ScriptException e) {
302
                    CompileErrorException ce = new CompileErrorException(e.getMessage(), this.getName(), e.getLineNumber(), e.getColumnNumber(), e);
303
                    notifyErrors(ce, "compile");
304
                    throw ce;
305
                } catch (Throwable e) {
306
                    CompileErrorException ce = new CompileErrorException(e.getMessage(), this.getName(), e);
307
                    notifyErrors(new Exception(e), "compile");
308
                    throw ce;
309
                }
310
            } else {
311
                String preparedCode = this.getCode();
312
                try {
313
                    engine.eval(preparedCode);
314
                } catch (ScriptException e) {
315
                    CompileErrorException ce = new CompileErrorException(e.getMessage(), this.getName(), e.getLineNumber(), e.getColumnNumber(), e);
316
                    notifyErrors(ce, "compile");
317
                    throw ce;
318
                }
319
            }
320
        }
321
    }
322

    
323
    public void addDisposable(Disposable disposable) {
324
        //pass
325
    }
326

    
327
    /**
328
     * Run the main function of this script.
329
     * This method is created by familiarity when running the script from another script.
330
     * @return 
331
     */
332
    public Object main() {
333
        return this.run(null);
334
    }
335

    
336
    /**
337
     * Run the main function of this script.
338
     * This method is created by familiarity when running the script from another script.
339
     * @return 
340
     */
341
    public Object main(Object... args) {
342
        return this.run(args);
343
    }
344

    
345
        
346
    public Object run() {
347
        return this.run(null);
348
    }
349

    
350
    @Override
351
    public Object run(Object args[]) {
352
        if (args == null) {
353
            args = new Object[]{};
354
        }
355
        this.compile();
356
        return this.invokeFunction(this.getMainName(), args);
357
    }
358

    
359
    @Override
360
    public Object invokeFunction(final String name, Object args[]) {
361
        if (this.getEngine() instanceof Invocable) {
362
            Invocable invocable = (Invocable) this.getEngine();
363
            this.compile();
364
            if (args == null) {
365
                args = new Object[]{};
366
            }
367
            try {
368
                return invocable.invokeFunction(name, args);
369
            } catch (ScriptException e) {
370
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getName(), e.getLineNumber(), e.getColumnNumber(), e);
371
                notifyErrors(ee, "invoke");
372
                throw ee;
373

    
374
            } catch (Error|Exception e) {
375
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getName(), e);
376
                notifyErrors(ee, "invoke");
377
                throw ee;
378
            }
379
        } else {
380
            return null;
381
        }
382
    }
383

    
384
    @Override
385
    public Object invokeMethod(final Object obj, final String name, Object[] args)
386
            throws NoSuchMethodException {
387

    
388
        if (this.getEngine() instanceof Invocable) {
389
            Invocable invocable = (Invocable) this.getEngine();
390
            this.compile();
391
            if (args == null) {
392
                args = new Object[]{};
393
            }
394
            try {
395
                return invocable.invokeMethod(obj, name, args);
396
            } catch (ScriptException e) {
397
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getName(), e.getLineNumber(), e.getColumnNumber(), e);
398
                notifyErrors(ee, "invoke");
399
                throw ee;
400
            } catch (Throwable e) {
401
                ExecuteErrorException ee = new ExecuteErrorException(e.getMessage(), this.getName(), e);
402
                notifyErrors(ee, "invoke");
403
                throw ee;
404
            }
405
        } else {
406
            return null;
407
        }
408
    }
409

    
410
    @Override
411
    public File getResource(String filename) {
412
        return new File(this.getParent().getFile(), filename);
413
    }
414

    
415
    @Override
416
    public String getMimeType() {
417
        return "text/"+ this.getLangName();
418
    }
419

    
420
    class ScriptTask extends AbstractMonitorableTask {
421

    
422
        ScriptingBaseScript script = null;
423
        Object[] args = null;
424

    
425
        protected ScriptTask(ScriptingBaseScript script, Object[] args) {
426
            super(script.getName(), false);
427
            this.args = args;
428
            this.script = script;
429
            this.script.put("task", this);
430
            this.script.put("taskStatus", this.getTaskStatus());
431
        }
432

    
433
        @Override
434
        public void run() {
435
            try {
436
                console_println("Running script " + this.script.getName() + ".");
437
                script.run(this.args);
438
                console_println("Script " + this.script.getName() + " terminated.");
439
            } catch (Throwable e) {
440
                console_println("Stript " + this.script.getName() + " aborted.");
441
            } finally {
442
                this.taskStatus.terminate();
443
                try {
444
                    Thread.sleep(3000);
445
                } catch (InterruptedException e) {
446
                    // Ignore
447
                }
448
                this.taskStatus.remove();
449
            }
450
        }
451

    
452
        public void showTaskStatus() {
453
            this.taskStatus.add();
454
        }
455
    }
456

    
457
    @Override
458
    public void runAsTask(Object[] args) {
459
        ScriptTask task = new ScriptTask(this, args);
460
        task.start();
461
    }
462

    
463
    @Override
464
    public boolean remove() {
465
        boolean r = true;
466
        File folder = this.getParent().getFile();
467
        File f = new File(folder, this.getId() + ".inf");
468
        try {
469
            FileUtils.forceDelete(f);
470
        } catch (IOException e) {
471
            logger.warn("Can't remove inf file '" + f.getAbsolutePath() + "'.", e);
472
            r = false;
473
        }
474
        try {
475
            f = new File(folder, this.getId() + this.getExtension());
476
            FileUtils.forceDelete(f);
477
        } catch (IOException e) {
478
            logger.warn("Can't remove code file '" + f.getAbsolutePath() + "'.", e);
479
            r = false;
480
        }
481
        return r;
482
    }
483

    
484
    @Override
485
    public void create(ScriptingFolder folder, String id, String language) {
486
        this.setParent(folder);
487
        this.setId(id);
488
        if (language == null) {
489
            this.setLangName("python");
490
        } else {
491
            this.setLangName(language);
492
        }
493
        this.setExtension(this.manager.getExtensionOfLanguage(getLangName()));
494

    
495
        File file = new File(folder.getFile(), id + ".inf");
496
        try {
497
            file.createNewFile();
498
        } catch (IOException e) {
499
            logger.warn("Can't create file of the dialog in '" + file.getAbsolutePath() + "'.", e);
500
        }
501
        String template = this.getNewTemplate();
502
        if( template != null ) {
503
            this.setCode(template);
504
        }
505
        this.save();
506
    }
507

    
508
    public String getNewTemplate() {
509
        String name = "org/gvsig/scripting/langs/"+this.getLangName().toLowerCase()+"/new_template.txt";
510
        try {
511
            InputStream template = this.getClass().getClassLoader().getResourceAsStream(name);
512
            if( template == null ) {
513
                return null;
514
            }
515
            List<String> lines = IOUtils.readLines(template);
516
            return StringUtils.join(lines, "\n");
517
        } catch (Exception ex) {
518
            logger.warn("Can't load new-template from '"+name+"'.",ex);
519
            return null;
520
        }
521
    }
522
    
523
    public ScriptingUnit get(String name) {
524
        return this.manager.getScript(name);
525
    }
526

    
527
    public ScriptingUnit get(File file) {
528
        return this.manager.getScript(file);
529
    }
530

    
531
    @Override
532
    public boolean move(ScriptingFolder target) {
533
        if (! manager.validateUnitId(target, this.getId()) ) {
534
            logger.info("Can't move script '"+this.getId()+"' to '"+target.getFile().getAbsolutePath()+"', is not valid.");
535
            return false;
536
        }
537
        if( !this.isSaved() ) {
538
            logger.info("Can't move script '"+this.getId()+"', is not saved.");
539
            return false;
540
        }
541
        try {
542
            File codefile = this.getFileResource(this.extension);
543
            FileUtils.moveFileToDirectory(this.getFile(), target.getFile(),true);
544
            FileUtils.moveFileToDirectory(codefile, target.getFile(), true);
545
            this.parent = target;
546
            this.load(target, id);
547
        } catch (IOException ex) {
548
            logger.info("Can't move script '"+this.getId()+"' to '"+target.getFile().getAbsolutePath()+"', "+ex.getMessage(),ex);
549
            return false;
550
        }
551
        return true;
552
    }
553

    
554
    @Override
555
    public boolean rename(String newId) {
556
        if (! manager.validateUnitId(this.getParent(), newId) ) {
557
            logger.info("Can't rename script '"+this.getId()+"', target id '"+newId+"' is not valid.");
558
            return false;
559
        }
560
        if( !this.isSaved() ) {
561
            logger.info("Can't rename script '"+this.getId()+"', is not saved.");
562
            return false;
563
        }
564
        try {
565
            ScriptingFolder target = this.getParent();
566
            File codefile = this.getFileResource(this.extension);
567
            FileUtils.moveFile(this.getFile(),  new File(target.getFile(),newId+".inf") );
568
            FileUtils.moveFile(codefile, new File(target.getFile(),newId+this.extension));
569
            this.setId(newId);
570
            this.saveInfo();
571
            this.load(target, id);
572
        } catch (IOException ex) {
573
            logger.info("Can't rename script '"+this.getId()+"' to '"+newId+"', "+ex.getMessage(),ex);
574
            return false;
575
        }        
576
        return true;
577
    }
578

    
579
}