Making the view work with all instances of the PHPEditor. Also making a cleanup of...
[phpeclipse.git] / net.sourceforge.phpeclipse.phpmanual / src / net / sourceforge / phpeclipse / phpmanual / views / PHPManualView.java
1 package net.sourceforge.phpeclipse.phpmanual.views;
2
3 import java.io.BufferedReader;
4 import java.io.FileNotFoundException;
5 import java.io.FileReader;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.net.URL;
9 import java.util.ArrayList;
10 import java.util.zip.ZipEntry;
11 import java.util.zip.ZipFile;
12
13 import net.sourceforge.phpdt.internal.ui.text.JavaWordFinder;
14 import net.sourceforge.phpdt.internal.ui.viewsupport.ISelectionListenerWithAST;
15 import net.sourceforge.phpdt.internal.ui.viewsupport.SelectionListenerWithASTManager;
16 import net.sourceforge.phpdt.phphelp.PHPHelpPlugin;
17 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
18 import net.sourceforge.phpeclipse.phpeditor.PHPEditor;
19 import net.sourceforge.phpeclipse.phpmanual.PHPManualUIPlugin;
20
21 import org.eclipse.core.runtime.Path;
22 import org.eclipse.core.runtime.Platform;
23 import org.eclipse.jface.text.IDocument;
24 import org.eclipse.jface.text.IRegion;
25 import org.eclipse.jface.text.ITextSelection;
26 import org.eclipse.jface.viewers.ISelection;
27 import org.eclipse.swt.SWT;
28 import org.eclipse.swt.widgets.Composite;
29 import org.eclipse.swt.widgets.Display;
30 import org.eclipse.swt.browser.Browser;
31 import org.eclipse.ui.IEditorPart;
32 import org.eclipse.ui.ISelectionListener;
33 import org.eclipse.ui.IWorkbenchPart;
34 import org.eclipse.ui.part.ViewPart;
35 import org.htmlparser.Node;
36 import org.htmlparser.Parser;
37 import org.htmlparser.tags.Div;
38 import org.htmlparser.util.ParserException;
39 import org.htmlparser.visitors.TagFindingVisitor;
40 import org.osgi.framework.Bundle;
41
42 /**
43  * This ViewPart is the implementation of the idea of having the 
44  * PHP Manual easily accessible while coding. It shows the
45  * under-cursor function's reference inside a browser.
46  * <p>
47  * The view listens to selection changes both in the (1)workbench, to
48  * know when the user changes between the instances of the PHPEditor
49  * or when a new instance is created; and in the (2)PHPEditor, to know
50  * when the user changes the cursor position. This explains the need
51  * to implement both ISelectionListener and ISelectionListenerWithAST.
52  * <p>
53  * Up to now, the ViewPart show reference pages from HTML stored in the
54  * doc.zip file from the net.sourceforge.phpeclipse.phphelp plugin. It
55  * also depends on net.sourceforge.phpeclipse.phpmanual.htmlparser to
56  * parse these HTML files.
57  * <p>
58  * @author scorphus
59  */
60 public class PHPManualView extends ViewPart implements ISelectionListener, ISelectionListenerWithAST {
61
62         /**
63          * The ViewPart's browser
64          */
65         private Browser browser;
66
67         /**
68          * A reference to store last active editor to know when we've
69          * got a new instance of the PHPEditor
70          */
71         private PHPEditor lastEditor;
72
73         /**
74          * The path to the doc.zip file containing the PHP Manual
75          * in HTML format
76          */
77         private final Path docPath = new Path("doc.zip"); 
78
79         /**
80          * The constructor.
81          */
82         public PHPManualView() {
83         }
84
85         /**
86          * This method initializes the ViewPart. It instantiates components
87          * and add listeners
88          * 
89          * @param parent The parent control
90          */
91         public void createPartControl(Composite parent) {
92                 browser = new Browser(parent, SWT.NONE);
93                 parent.pack();
94                 if ((lastEditor = getJavaEditor()) != null) {
95                         SelectionListenerWithASTManager.getDefault().addListener(lastEditor, this);
96                 }
97                 getSite().getWorkbenchWindow().getSelectionService()
98                                 .addSelectionListener(PHPeclipsePlugin.EDITOR_ID, this);
99         }
100
101         /**
102          * Cleanup to remove the selection listener
103          */
104         public void dispose() {
105                 getSite().getWorkbenchWindow().getSelectionService()
106                                 .removeSelectionListener(PHPeclipsePlugin.EDITOR_ID, this);
107         }
108
109         /**
110          * Passing the focus request to the viewer's control.
111          */
112         public void setFocus() {
113                 browser.setFocus();
114         }
115
116         /**
117          * Treats selection changes from the PHPEditor
118          */
119         public void selectionChanged(IEditorPart part, ITextSelection selection) {
120                 IDocument document = ((PHPEditor)part).getViewer().getDocument();
121                 int offset = selection.getOffset();
122                 IRegion iRegion = JavaWordFinder.findWord(document, offset);
123                 try {
124                         final String wordStr = document.get(iRegion.getOffset(),
125                                         iRegion.getLength());
126                         showReference(wordStr);
127                 } catch (Exception e) {
128                         e.printStackTrace();
129                 }
130         }
131
132         /**
133          * Treats selection changes from the workbench. When part is new
134          * instance of PHPEditor it gets a listener attached
135          */
136         public void selectionChanged(IWorkbenchPart part, ISelection selection) {
137                 if (!((PHPEditor)part).equals(lastEditor)) {
138                         SelectionListenerWithASTManager.getDefault().addListener((PHPEditor)part, this);
139                         lastEditor = (PHPEditor)part;
140                 }
141         }
142
143         /**
144          * Updates the browser with the reference page for a given function
145          * 
146          * @param funcName Function name
147          */
148         private void showReference(final String funcName) {
149                 System.out.println("Show reference for " + funcName);
150                 new Thread(new Runnable() {
151                         public void run() {
152                                 Display.getDefault().asyncExec(new Runnable() {
153                                         public void run() {
154                                                 String html = getHtmlSource(funcName);
155                                                 browser.setText(html);
156                                         }
157                                 });
158                         }
159                 }).start();
160         }
161
162         /**
163          * Filters the function's reference page extracting only parts of it
164          * 
165          * @param source HTML source of the reference page
166          * @return HTML source of reference page
167          */
168         private String filterHtmlSource(String source) {
169                 try {
170                         Parser parser = new Parser(source);
171                         String [] tagsToBeFound = {"DIV"};
172                         ArrayList<String> classList = new ArrayList<String>(6);
173                         classList.add("refnamediv");
174                         classList.add("refsect1 description");
175                         classList.add("refsect1 parameters");
176                         classList.add("refsect1 returnvalues");
177                         classList.add("refsect1 examples");
178                         classList.add("refsect1 seealso");
179                         classList.add("refsect1 u");
180                         TagFindingVisitor visitor = new TagFindingVisitor(tagsToBeFound);
181                         parser.visitAllNodesWith(visitor);
182                         Node [] allPTags = visitor.getTags(0);
183                         StringBuilder output = new StringBuilder();
184                         for (int i = 0; i < allPTags.length; i++) {
185                                 String tagClass = ((Div)allPTags[i]).getAttribute("class");
186                                 if (classList.contains(tagClass)) {
187                                         output.append(allPTags[i].toHtml());
188                                 }
189                         }
190                         return output.toString().replace("—", "-");
191                         //.replace("<h3 class=\"title\">Description</h3>", " ");
192                 } catch (ParserException e) {
193                         e.printStackTrace();
194                 }
195                 return "";
196         }
197
198         /**
199          * Reads the template that defines the style of the reference page
200          * shown inside the view's browser
201          * 
202          * @return HTML source of the template
203          */
204         public String getRefPageTemplate() {
205                 Bundle bundle = Platform.getBundle(PHPManualUIPlugin.PLUGIN_ID);
206                 URL fileURL = Platform.find(bundle, new Path("templates"));
207                 StringBuffer contents = new StringBuffer();
208                 BufferedReader input = null;
209                 try {
210                         URL resolve = Platform.resolve(fileURL);
211                         input = new BufferedReader(new FileReader(resolve.getPath()+"/refpage.html"));
212                         String line = null;
213                         while ((line = input.readLine()) != null){
214                                 contents.append(line);
215                         }
216                 }
217                 catch (FileNotFoundException e) {
218                         e.printStackTrace();
219                 } catch (IOException e) {
220                         e.printStackTrace();
221                 }
222                 finally {
223                         try {
224                                 if (input!= null) {
225                                         input.close();
226                                 }
227                         }
228                         catch (IOException ex) {
229                                 ex.printStackTrace();
230                         }
231                 }
232                 return contents.toString();
233         }
234
235         /**
236          * Looks for the function's reference page inside the doc.zip file
237          * and returns a filtered HTML source of it embedded in the template
238          *  
239          * @param funcName Function name
240          * @return HTML source of reference page
241          */
242         public String getHtmlSource(String funcName) {
243                 Bundle bundle = Platform.getBundle(PHPHelpPlugin.PLUGIN_ID);
244                 URL fileURL = Platform.find(bundle, docPath);
245                 byte[] b = null;
246                 try {
247                         URL resolve = Platform.resolve(fileURL);
248                         ZipFile docFile = new ZipFile(resolve.getPath());
249                         ZipEntry entry = docFile.getEntry("doc/function."+funcName.replace('_', '-')+".html");
250                         InputStream ref = docFile.getInputStream(entry);
251                         b = new byte[(int)entry.getSize()];
252                         ref.read(b, 0, (int)entry.getSize());
253                 } catch (IOException e) {
254                         return "<html></html>";
255                 }
256                 if (b != null) {
257                         String reference = filterHtmlSource(new String(b));
258                         String refPageTpl = getRefPageTemplate();
259                         refPageTpl = refPageTpl.replace("{title}", funcName);
260                         refPageTpl = refPageTpl.replace("{reference}", reference);
261                         return refPageTpl;
262                 }
263                 return "<html></html>";
264         }
265
266         /**
267          * Returns the currently active java editor, or <code>null</code> if it
268          * cannot be determined.
269          * 
270          * @return the currently active java editor, or <code>null</code>
271          */
272         private PHPEditor getJavaEditor() {
273                 try {
274                         IEditorPart part = PHPeclipsePlugin.getActivePage().getActiveEditor();
275                         if (part instanceof PHPEditor)
276                                 return (PHPEditor) part;
277                         else
278                                 return null;
279                 } catch (Exception e) {
280                         return null;
281                 }
282         }
283
284 }