View Javadoc

1   // Copyright (C) 2007 - 2009 Philip Aston
2   // All rights reserved.
3   //
4   // This file is part of The Grinder software distribution. Refer to
5   // the file LICENSE which is part of The Grinder distribution for
6   // licensing details. The Grinder distribution is available on the
7   // Internet at http://grinder.sourceforge.net/
8   //
9   // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
13  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
14  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
20  // OF THE POSSIBILITY OF SUCH DAMAGE.
21  
22  package net.grinder.console.editor;
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.util.ArrayList;
27  import java.util.List;
28  import java.util.StringTokenizer;
29  
30  import net.grinder.common.GrinderException;
31  import net.grinder.console.distribution.AgentCacheState;
32  
33  /**
34   * Handles opening a file in external editor.
35   *
36   * @author Philip Aston
37   */
38  class ExternalEditor {
39  
40    private static final ThreadGroup s_threadGroup =
41      new ThreadGroup("ExternalEditor");
42  
43    static final ThreadGroup getThreadGroup() {
44      return s_threadGroup;
45    }
46  
47    private final AgentCacheState m_agentCacheState;
48    private final EditorModel m_editorModel;
49    private final File m_command;
50    private final String m_arguments;
51  
52    public ExternalEditor(AgentCacheState agentCacheState,
53                          EditorModel editorModel,
54                          File command,
55                          String arguments) {
56      m_agentCacheState = agentCacheState;
57      m_editorModel = editorModel;
58      m_command = command;
59      m_arguments = arguments;
60    }
61  
62  
63    String[] fileToCommandLine(File file) {
64      final List<String> result = new ArrayList<String>();
65      result.add(m_command.getAbsolutePath());
66  
67      boolean fileTemplateFound = false;
68  
69      if (m_arguments != null) {
70        final StringTokenizer tokenizer = new StringTokenizer(m_arguments);
71  
72        while (tokenizer.hasMoreElements()) {
73          final String token = tokenizer.nextToken();
74  
75          final String argument = token.replaceAll("%f", file.getAbsolutePath());
76          result.add(argument);
77  
78          fileTemplateFound |= !argument.equals(token);
79        }
80      }
81  
82      if (!fileTemplateFound) {
83        result.add(file.getAbsolutePath());
84      }
85  
86      return result.toArray(new String[result.size()]);
87    }
88  
89    public void open(final File file) throws IOException {
90      final long originalModificationTime = file.lastModified();
91  
92      final Process exec = Runtime.getRuntime().exec(
93        fileToCommandLine(file),
94        null,
95        file.getParentFile());
96  
97      final Runnable handleCompletion = new Runnable() {
98        public void run() {
99          try {
100           // We ignore the process result when figuring out whether the
101           // editor changed the file. I don't trust all editors to return
102           // 0 on success.
103           exec.waitFor();
104         }
105         catch (InterruptedException e) {
106           // This thread has been interrupted, silently exit.
107           return;
108         }
109 
110         // If file no longer exists, lastModified will be 0.
111         final long lastModified = file.lastModified();
112 
113         if (lastModified > originalModificationTime) {
114           m_agentCacheState.setNewFileTime(lastModified);
115 
116           // If there is an existing, clean buffer refresh it from the file.
117           final Buffer buffer = m_editorModel.getBufferForFile(file);
118 
119           if (buffer != null && !buffer.isDirty()) {
120             try {
121               buffer.load();
122             }
123             catch (GrinderException e) {
124               // Ignore.
125             }
126           }
127         }
128       }
129     };
130 
131     final Thread thread = new Thread(getThreadGroup(),
132                                      handleCompletion,
133                                      "External edit of " + file);
134     thread.setDaemon(true);
135     thread.start();
136   }
137 }