1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package net.grinder.console.editor;
23
24 import static java.util.Arrays.asList;
25
26 import java.io.File;
27 import java.io.IOException;
28 import java.util.Collections;
29 import java.util.EventListener;
30 import java.util.HashMap;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Map;
34
35 import net.grinder.common.GrinderProperties;
36 import net.grinder.common.GrinderProperties.PersistenceException;
37 import net.grinder.console.common.ConsoleException;
38 import net.grinder.console.common.DisplayMessageConsoleException;
39 import net.grinder.console.common.Resources;
40 import net.grinder.console.distribution.AgentCacheState;
41 import net.grinder.console.distribution.FileChangeWatcher;
42 import net.grinder.console.distribution.FileChangeWatcher.FileChangedListener;
43 import net.grinder.util.ListenerSupport;
44
45
46
47
48
49
50
51 public final class EditorModel {
52
53 private static final List<String> s_knownScriptTypes =
54 asList("py", "clj");
55
56 private final Resources m_resources;
57 private final TextSource.Factory m_textSourceFactory;
58 private final AgentCacheState m_agentCacheState;
59
60 private final ListenerSupport<Listener> m_listeners =
61 new ListenerSupport<Listener>();
62
63
64 private final LinkedList<Buffer> m_bufferList = new LinkedList<Buffer>();
65
66
67 private final Map<File, Buffer> m_fileBuffers =
68 Collections.synchronizedMap(new HashMap<File, Buffer>());
69
70
71 private int m_nextNewBufferNameIndex = 0;
72
73
74 private Buffer m_selectedBuffer;
75
76
77 private File m_selectedProperties;
78
79
80 private File m_selectedFile;
81
82
83 private ExternalEditor m_externalEditor;
84
85
86
87
88
89
90
91
92
93 public EditorModel(Resources resources,
94 TextSource.Factory textSourceFactory,
95 AgentCacheState agentCacheState,
96 FileChangeWatcher fileChangeWatcher) {
97 m_resources = resources;
98 m_textSourceFactory = textSourceFactory;
99 m_agentCacheState = agentCacheState;
100
101 fileChangeWatcher.addFileChangedListener(new FileChangedListener() {
102 public void filesChanged(File[] files) {
103 synchronized (m_fileBuffers) {
104 for (int i = 0; i < files.length; ++i) {
105 final Buffer buffer = getBufferForFile(files[i]);
106
107 if (buffer != null && !buffer.isUpToDate()) {
108 fireBufferNotUpToDate(buffer);
109 }
110
111 parseSelectedProperties(files[i]);
112 }
113 }
114 }
115 });
116 }
117
118
119
120
121
122
123 public Buffer getSelectedBuffer() {
124 synchronized (this) {
125 return m_selectedBuffer;
126 }
127 }
128
129
130
131
132 public void selectNewBuffer() {
133 final Buffer buffer = new BufferImplementation(m_resources,
134 m_textSourceFactory.create(),
135 createNewBufferName());
136 addBuffer(buffer);
137
138 selectBuffer(buffer);
139 }
140
141
142
143
144
145
146
147
148
149
150 public Buffer selectBufferForFile(File file) throws ConsoleException {
151 final Buffer existingBuffer = getBufferForFile(file);
152 final Buffer buffer;
153
154 if (existingBuffer != null) {
155 buffer = existingBuffer;
156
157 selectBuffer(buffer);
158
159 if (!buffer.isUpToDate()) {
160
161
162
163 fireBufferNotUpToDate(buffer);
164 }
165 }
166 else {
167 buffer = new BufferImplementation(m_resources,
168 m_textSourceFactory.create(),
169 file);
170 buffer.load();
171 addBuffer(buffer);
172
173 m_fileBuffers.put(file, buffer);
174
175 selectBuffer(buffer);
176 }
177
178 return buffer;
179 }
180
181
182
183
184
185
186
187
188 public Buffer getBufferForFile(File file) {
189 return m_fileBuffers.get(file);
190 }
191
192
193
194
195
196
197 public Buffer[] getBuffers() {
198 synchronized (m_bufferList) {
199 return m_bufferList.toArray(new Buffer[m_bufferList.size()]);
200 }
201 }
202
203
204
205
206
207
208 public boolean isABufferDirty() {
209 final Buffer[] buffers = getBuffers();
210
211 for (int i = 0; i < buffers.length; ++i) {
212 if (buffers[i].isDirty()) {
213 return true;
214 }
215 }
216
217 return false;
218 }
219
220
221
222
223
224
225 public void selectBuffer(Buffer buffer) {
226 final Buffer oldBuffer = getSelectedBuffer();
227
228 if (buffer == null || !buffer.equals(oldBuffer)) {
229
230 synchronized (this) {
231 m_selectedBuffer = buffer;
232 }
233
234 if (oldBuffer != null) {
235 fireBufferStateChanged(oldBuffer);
236 }
237
238 if (buffer != null) {
239 fireBufferStateChanged(buffer);
240 }
241 }
242 }
243
244
245
246
247
248
249 public void closeBuffer(final Buffer buffer) {
250 final boolean removed;
251
252 synchronized (m_bufferList) {
253 removed = m_bufferList.remove(buffer);
254 }
255
256 if (removed) {
257 final File file = buffer.getFile();
258
259 if (buffer.equals(getBufferForFile(file))) {
260 m_fileBuffers.remove(file);
261 }
262
263 if (buffer.equals(getSelectedBuffer())) {
264 final Buffer bufferToSelect;
265
266 synchronized (m_bufferList) {
267 final int numberOfBuffers = m_bufferList.size();
268
269 bufferToSelect = numberOfBuffers > 0 ?
270 (Buffer)m_bufferList.get(numberOfBuffers - 1) : null;
271 }
272
273 selectBuffer(bufferToSelect);
274 }
275
276 m_listeners.apply(
277 new ListenerSupport.Informer<Listener>() {
278 public void inform(Listener l) { l.bufferRemoved(buffer); }
279 });
280 }
281 }
282
283
284
285
286
287
288 public File getSelectedPropertiesFile() {
289 synchronized (this) {
290 return m_selectedProperties;
291 }
292 }
293
294
295
296
297
298
299 public void setSelectedPropertiesFile(final File selectedProperties) {
300 synchronized (this) {
301 m_selectedProperties = selectedProperties;
302
303 if (selectedProperties == null) {
304 m_selectedFile = null;
305 }
306 }
307
308 parseSelectedProperties(selectedProperties);
309 }
310
311 private void addBuffer(final Buffer buffer) {
312 buffer.getTextSource().addListener(new TextSource.Listener() {
313 public void textSourceChanged(boolean dirtyStateChanged) {
314 if (dirtyStateChanged) {
315 fireBufferStateChanged(buffer);
316 }
317 }
318 });
319
320 buffer.addListener(
321 new BufferImplementation.Listener() {
322 public void bufferSaved(Buffer savedBuffer, File oldFile) {
323 final File newFile = savedBuffer.getFile();
324
325 m_agentCacheState.setNewFileTime(newFile.lastModified());
326
327 if (!newFile.equals(oldFile)) {
328 if (oldFile != null) {
329 m_fileBuffers.remove(oldFile);
330 }
331
332 m_fileBuffers.put(newFile, savedBuffer);
333
334
335
336 fireBufferStateChanged(savedBuffer);
337 }
338
339 parseSelectedProperties(newFile);
340 }
341 }
342 );
343
344 synchronized (m_bufferList) {
345 m_bufferList.add(buffer);
346 }
347
348 m_listeners.apply(
349 new ListenerSupport.Informer<Listener>() {
350 public void inform(Listener l) { l.bufferAdded(buffer); }
351 });
352 }
353
354 private void fireBufferStateChanged(final Buffer buffer) {
355 m_listeners.apply(
356 new ListenerSupport.Informer<Listener>() {
357 public void inform(Listener l) { l.bufferStateChanged(buffer); }
358 });
359 }
360
361
362
363
364
365 private void fireBufferNotUpToDate(final Buffer buffer) {
366 m_listeners.apply(
367 new ListenerSupport.Informer<Listener>() {
368 public void inform(Listener l) { l.bufferNotUpToDate(buffer); }
369 });
370 }
371
372 private String createNewBufferName() {
373
374 final String prefix = m_resources.getString("newBuffer.text");
375
376 synchronized (this) {
377 try {
378 if (m_nextNewBufferNameIndex == 0) {
379 return prefix;
380 }
381 else {
382 return prefix + " " + m_nextNewBufferNameIndex;
383 }
384 }
385 finally {
386 ++m_nextNewBufferNameIndex;
387 }
388 }
389 }
390
391 private void parseSelectedProperties(File file) {
392
393 if (file != null && file.equals(getSelectedPropertiesFile())) {
394 File selectedFile;
395
396 try {
397 final GrinderProperties properties = new GrinderProperties(file);
398
399 selectedFile =
400 properties.resolveRelativeFile(
401 properties.getFile(GrinderProperties.SCRIPT,
402 GrinderProperties.DEFAULT_SCRIPT))
403 .getCanonicalFile();
404 }
405 catch (PersistenceException e) {
406 selectedFile = null;
407 }
408 catch (IOException e) {
409 selectedFile = null;
410 }
411
412 synchronized (this) {
413 m_selectedFile = selectedFile;
414 }
415 }
416 }
417
418
419
420
421
422
423 public void addListener(Listener listener) {
424 m_listeners.add(listener);
425 }
426
427
428
429
430
431
432
433
434 public boolean isScriptFile(File f) {
435 if (f != null &&
436 (!f.exists() || f.isFile())) {
437
438 final int lastDot = f.getName().lastIndexOf('.');
439
440 if (lastDot >= 0) {
441 final String suffix = f.getName().substring(lastDot + 1).toLowerCase();
442
443 return s_knownScriptTypes.contains(suffix);
444 }
445 }
446
447 return false;
448 }
449
450
451
452
453
454
455
456
457 public boolean isPropertiesFile(File f) {
458 return
459 f != null &&
460 (!f.exists() || f.isFile()) &&
461 f.getName().toLowerCase().endsWith(".properties");
462 }
463
464
465
466
467
468
469
470
471 public boolean isSelectedScript(File f) {
472
473
474 synchronized (this) {
475 return f != null && f.equals(m_selectedFile);
476 }
477 }
478
479
480
481
482
483
484
485 public boolean isBoringFile(File f) {
486 if (f == null) {
487 return false;
488 }
489
490 final String name = f.getName().toLowerCase();
491
492 return
493 f.isHidden() ||
494 name.endsWith(".class") ||
495 name.startsWith("~") ||
496 name.endsWith("~") ||
497 name.startsWith("#") ||
498 name.endsWith(".exe") ||
499 name.endsWith(".gif") ||
500 name.endsWith(".jpeg") ||
501 name.endsWith(".jpg") ||
502 name.endsWith(".tiff");
503 }
504
505
506
507
508
509
510
511
512 public void openWithExternalEditor(final File file) throws ConsoleException {
513 final ExternalEditor externalEditor;
514
515 synchronized (this) {
516 externalEditor = m_externalEditor;
517 }
518
519 if (externalEditor == null) {
520 throw new DisplayMessageConsoleException(m_resources,
521 "externalEditorNotSet.text");
522 }
523
524 try {
525 externalEditor.open(file);
526 }
527 catch (IOException e) {
528 throw new DisplayMessageConsoleException(m_resources,
529 "externalEditError.text",
530 e);
531 }
532 }
533
534
535
536
537
538
539
540
541
542
543
544
545
546 public void setExternalEditor(File command, String arguments) {
547 final ExternalEditor externalEditor;
548 if (command == null) {
549 externalEditor = null;
550 }
551 else {
552 externalEditor =
553 new ExternalEditor(m_agentCacheState, this, command, arguments);
554 }
555
556 synchronized (this) {
557 m_externalEditor = externalEditor;
558 }
559 }
560
561
562
563
564 public interface Listener extends EventListener {
565
566
567
568
569
570
571 void bufferAdded(Buffer buffer);
572
573
574
575
576
577
578
579
580 void bufferStateChanged(Buffer buffer);
581
582
583
584
585
586
587
588 void bufferNotUpToDate(Buffer buffer);
589
590
591
592
593
594
595 void bufferRemoved(Buffer buffer);
596 }
597
598
599
600
601 public abstract static class AbstractListener implements Listener {
602
603
604
605
606 @Override public void bufferAdded(Buffer buffer) { }
607
608
609
610
611 @Override public void bufferStateChanged(Buffer buffer) { }
612
613
614
615
616 @Override public void bufferNotUpToDate(Buffer buffer) { }
617
618
619
620
621 @Override public void bufferRemoved(Buffer buffer) { }
622 }
623 }