The CTRL+Space did not show function details. This was missed when i first fixed...
[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.FileLocator;
22 import org.eclipse.core.runtime.Path;
23 import org.eclipse.core.runtime.Platform;
24 import org.eclipse.jface.text.IDocument;
25 import org.eclipse.jface.text.IRegion;
26 import org.eclipse.jface.text.ITextSelection;
27 import org.eclipse.jface.viewers.ISelection;
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.browser.Browser;
30 import org.eclipse.swt.browser.LocationAdapter;
31 import org.eclipse.swt.browser.LocationEvent;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Display;
34 import org.eclipse.ui.IEditorPart;
35 import org.eclipse.ui.INullSelectionListener;
36 import org.eclipse.ui.IWorkbenchPart;
37 import org.eclipse.ui.part.ViewPart;
38 import org.htmlparser.Node;
39 import org.htmlparser.Parser;
40 import org.htmlparser.tags.Div;
41 import org.htmlparser.util.ParserException;
42 import org.htmlparser.visitors.TagFindingVisitor;
43 import org.osgi.framework.Bundle;
44
45 /**
46  * This ViewPart is the implementation of the idea of having the 
47  * PHP Manual easily accessible while coding. It shows the
48  * under-cursor function's reference inside a browser.
49  * <p>
50  * The view listens to selection changes both in the (1)workbench, to
51  * know when the user changes between the instances of the PHPEditor
52  * or when a new instance is created; and in the (2)PHPEditor, to know
53  * when the user changes the cursor position. This explains the need
54  * to implement both ISelectionListener and ISelectionListenerWithAST.
55  * <p>
56  * Up to now, the ViewPart show reference pages from HTML stored in the
57  * doc.zip file from the net.sourceforge.phpeclipse.phphelp plugin. It
58  * also depends on net.sourceforge.phpeclipse.phpmanual.htmlparser to
59  * parse these HTML files.
60  * <p>
61  * @author scorphus
62  */
63 public class PHPManualView extends ViewPart implements INullSelectionListener, ISelectionListenerWithAST {
64
65         /**
66          * The ViewPart's browser
67          */
68         private Browser browser;
69
70         /**
71          * A reference to store last active editor to know when we've
72          * got a new instance of the PHPEditor
73          */
74         private PHPEditor lastEditor;
75
76         /**
77          * String that stores the last selected word
78          */
79         private String lastOccurrence = null;
80
81         /**
82          * The path to the doc.zip file containing the PHP Manual
83          * in HTML format
84          */
85         private final Path docPath = new Path("doc.zip"); 
86
87         /**
88          * The constructor.
89          */
90         public PHPManualView() {
91         }
92
93         /**
94          * This method initializes the ViewPart. It instantiates components
95          * and add listeners
96          * 
97          * @param parent The parent control
98          */
99         public void createPartControl(Composite parent) {
100                 browser = new Browser(parent, SWT.NONE);
101                 browser.addLocationListener(new LocationAdapter() {
102                         public void changing(LocationEvent event) {
103                                 String loc = event.location.toString();
104                                 if(!loc.equalsIgnoreCase("about:blank") && !loc.startsWith("jar:")) {
105                                         String func = loc.replaceAll("file:///", "");
106                                         func = func.replaceAll("#.+$", "");
107                                         String[] afunc = loc.split("\\.");
108                                         if(!afunc[1].equalsIgnoreCase(lastOccurrence)) {
109                                                 lastOccurrence = afunc[1];
110                                                 showLinkReference(func);
111                                                 event.doit = false;
112                                         }
113                                 }
114                         }
115                 });
116                 parent.pack();
117                 if ((lastEditor = getJavaEditor()) != null) {
118                         SelectionListenerWithASTManager.getDefault().addListener(lastEditor, this);
119                 }
120                 getSite().getWorkbenchWindow().getSelectionService()
121                                 .addPostSelectionListener(PHPeclipsePlugin.EDITOR_ID, this);
122         }
123
124         /**
125          * Cleanup to remove the selection listener
126          */
127         public void dispose() {
128                 getSite().getWorkbenchWindow().getSelectionService()
129                                 .removePostSelectionListener(PHPeclipsePlugin.EDITOR_ID, this);
130         }
131
132         /**
133          * Passing the focus request to the viewer's control.
134          */
135         public void setFocus() {
136                 browser.setFocus();
137         }
138
139         /**
140          * Treats selection changes from the PHPEditor
141          */
142         public void selectionChanged(IEditorPart part, ITextSelection selection) {
143                 IDocument document = ((PHPEditor)part).getViewer().getDocument();
144                 int offset = selection.getOffset();
145                 IRegion iRegion = JavaWordFinder.findWord(document, offset);
146                 if (document != null && iRegion != null) {
147                         try {
148                                 final String wordStr = document.get(iRegion.getOffset(),
149                                                 iRegion.getLength());
150                                 if (!wordStr.equalsIgnoreCase(lastOccurrence)) {
151                                         showReference(wordStr);                         
152                                         lastOccurrence = wordStr;
153                                 }
154                         } catch (Exception e) {
155                                 e.printStackTrace();
156                         }
157                 }
158         }
159
160         /**
161          * Treats selection changes from the workbench. When part is new
162          * instance of PHPEditor it gets a listener attached
163          */
164         public void selectionChanged(IWorkbenchPart part, ISelection selection) {
165                 if (part != null && !((PHPEditor)part).equals(lastEditor)) {
166                         SelectionListenerWithASTManager.getDefault().addListener((PHPEditor)part, this);
167                         lastEditor = (PHPEditor)part;
168                 } else {
169                         System.out.println(part);
170                 }
171         }
172
173         /**
174          * Updates the browser with the reference page for a given function
175          * 
176          * @param funcName Function name
177          */
178         private void showReference(final String funcName) {
179                 new Thread(new Runnable() {
180                         public void run() {
181                                 Display.getDefault().asyncExec(new Runnable() {
182                                         public void run() {
183                                                 String html = getHtmlSource(funcName);
184                                                 browser.setText(html);
185                                         }
186                                 });
187                         }
188                 }).start();
189         }
190         
191         /**
192          * Updates the browser with the reference page for a given function
193          * 
194          * @param funcName Function name
195          */
196         private void showLinkReference(final String funcName) {
197                 new Thread(new Runnable() {
198                         public void run() {
199                                 Display.getDefault().asyncExec(new Runnable() {
200                                         public void run() {
201                                                 String html = getLinkHtmlSource(funcName);
202                                                 browser.setText(html);
203                                         }
204                                 });
205                         }
206                 }).start();
207         }
208
209         /**
210          * Filters the function's reference page extracting only parts of it
211          * 
212          * @param source HTML source of the reference page
213          * @return HTML source of reference page
214          */
215         private String filterIniHtmlSource(String source) {
216                 try {
217                         Parser parser = new Parser(source);
218                         String [] tagsToBeFound = {"DIV"};
219                         ArrayList classList = new ArrayList(8);
220                         classList.add("section");
221                         classList.add("title");
222                         classList.add("refsect1 parameters");
223                         classList.add("refsect1 returnvalues");
224                         classList.add("refsect1 examples");
225                         classList.add("refsect1 seealso");
226                         classList.add("refsect1 u");
227                         TagFindingVisitor visitor = new TagFindingVisitor(tagsToBeFound);
228                         parser.visitAllNodesWith(visitor);
229                         Node [] allPTags = visitor.getTags(0);
230                         StringBuffer output = new StringBuffer();
231                         for (int i = 0; i < allPTags.length; i++) {
232                                 String tagClass = ((Div)allPTags[i]).getAttribute("class");
233                                 if (classList.contains(tagClass)) {
234                                         output.append(allPTags[i].toHtml());
235                                 }
236                         }
237                         return output.toString().replaceAll("—", "-");
238                         //.replace("<h3 class=\"title\">Description</h3>", " ");
239                 } catch (ParserException e) {
240                         e.printStackTrace();
241                 }
242                 return "";
243         }
244         
245         /**
246          * Filters the function's reference page extracting only parts of it
247          * 
248          * @param source HTML source of the reference page
249          * @return HTML source of reference page
250          */
251         private String filterLangHtmlSource(String source) {
252                 try {
253                         Parser parser = new Parser(source);
254                         String [] tagsToBeFound = {"DIV"};
255                         ArrayList classList = new ArrayList(8);
256                         classList.add("sect1");
257                         classList.add("title");
258                         classList.add("refsect1 parameters");
259                         classList.add("refsect1 returnvalues");
260                         classList.add("refsect1 examples");
261                         classList.add("refsect1 seealso");
262                         classList.add("refsect1 u");
263                         TagFindingVisitor visitor = new TagFindingVisitor(tagsToBeFound);
264                         parser.visitAllNodesWith(visitor);
265                         Node [] allPTags = visitor.getTags(0);
266                         StringBuffer output = new StringBuffer();
267                         for (int i = 0; i < allPTags.length; i++) {
268                                 String tagClass = ((Div)allPTags[i]).getAttribute("class");
269                                 if (classList.contains(tagClass)) {
270                                         output.append(allPTags[i].toHtml());
271                                 }
272                         }
273                         return output.toString().replaceAll("—", "-");
274                         //.replace("<h3 class=\"title\">Description</h3>", " ");
275                 } catch (ParserException e) {
276                         e.printStackTrace();
277                 }
278                 return "";
279         }
280         
281         /**
282          * Filters the function's reference page extracting only parts of it
283          * 
284          * @param source HTML source of the reference page
285          * @return HTML source of reference page
286          */
287         private String filterRefHtmlSource(String source) {
288                 try {
289                         Parser parser = new Parser(source);
290                         String [] tagsToBeFound = {"DIV"};
291                         ArrayList classList = new ArrayList(8);
292                         classList.add("partintro");
293                         classList.add("section");
294                         classList.add("title");
295                         classList.add("refsect1 parameters");
296                         classList.add("refsect1 returnvalues");
297                         classList.add("refsect1 examples");
298                         classList.add("refsect1 seealso");
299                         classList.add("refsect1 u");
300                         TagFindingVisitor visitor = new TagFindingVisitor(tagsToBeFound);
301                         parser.visitAllNodesWith(visitor);
302                         Node [] allPTags = visitor.getTags(0);
303                         StringBuffer output = new StringBuffer();
304                         for (int i = 0; i < allPTags.length; i++) {
305                                 String tagClass = ((Div)allPTags[i]).getAttribute("class");
306                                 if (classList.contains(tagClass)) {
307                                         output.append(allPTags[i].toHtml());
308                                 }
309                         }
310                         return output.toString().replaceAll("—", "-");
311                         //.replace("<h3 class=\"title\">Description</h3>", " ");
312                 } catch (ParserException e) {
313                         e.printStackTrace();
314                 }
315                 return "";
316         }
317         
318         /**
319          * Filters the function's reference page extracting only parts of it
320          * 
321          * @param source HTML source of the reference page
322          * @return HTML source of reference page
323          */
324         private String filterHtmlSource(String source) {
325                 try {
326                         Parser parser = new Parser(source);
327                         String [] tagsToBeFound = {"DIV"};
328                         ArrayList classList = new ArrayList(8);
329                         classList.add("refnamediv");
330                         classList.add("refsect1 description");
331                         classList.add("refsect1 parameters");
332                         classList.add("refsect1 returnvalues");
333                         classList.add("refsect1 examples");
334                         classList.add("refsect1 seealso");
335                         classList.add("refsect1 u");
336                         TagFindingVisitor visitor = new TagFindingVisitor(tagsToBeFound);
337                         parser.visitAllNodesWith(visitor);
338                         Node [] allPTags = visitor.getTags(0);
339                         StringBuffer output = new StringBuffer();
340                         for (int i = 0; i < allPTags.length; i++) {
341                                 String tagClass = ((Div)allPTags[i]).getAttribute("class");
342                                 if (classList.contains(tagClass)) {
343                                         output.append(allPTags[i].toHtml());
344                                 }
345                         }
346                         return output.toString().replaceAll("—", "-");
347                         //.replace("<h3 class=\"title\">Description</h3>", " ");
348                 } catch (ParserException e) {
349                         e.printStackTrace();
350                 }
351                 return "";
352         }
353         /**
354          * Reads the template that defines the style of the reference page
355          * shown inside the view's browser
356          * 
357          * @return HTML source of the template
358          */
359         public String getRefPageTemplate() {
360                 Bundle bundle = Platform.getBundle(PHPManualUIPlugin.PLUGIN_ID);
361                 URL fileURL = FileLocator.find(bundle, new Path("templates"), null);
362                 StringBuffer contents = new StringBuffer();
363                 BufferedReader input = null;
364                 try {
365                         URL resolve = FileLocator.resolve(fileURL);
366                         input = new BufferedReader(new FileReader(resolve.getPath()+"/refpage.html"));
367                         String line = null;
368                         while ((line = input.readLine()) != null){
369                                 contents.append(line);
370                         }
371                 }
372                 catch (FileNotFoundException e) {
373                         e.printStackTrace();
374                 } catch (IOException e) {
375                         e.printStackTrace();
376                 }
377                 finally {
378                         try {
379                                 if (input!= null) {
380                                         input.close();
381                                 }
382                         }
383                         catch (IOException ex) {
384                                 ex.printStackTrace();
385                         }
386                 }
387                 return contents.toString();
388         }
389
390         /**
391          * Replaces each substring of source string that matches the
392          * given pattern string with the given replace string
393          * 
394          * @param source The source string
395          * @param pattern The pattern string
396          * @param replace The replace string
397          * @return The resulting String
398          */
399         public static String replace(String source, String pattern, String replace) {
400                 if (source != null) {
401                         final int len = pattern.length();
402                         StringBuffer sb = new StringBuffer();
403                         int found = -1;
404                         int start = 0;
405                         while ((found = source.indexOf(pattern, start)) != -1) {
406                                 sb.append(source.substring(start, found));
407                                 sb.append(replace);
408                                 start = found + len;
409                         }
410                         sb.append(source.substring(start));
411                         return sb.toString();
412                 } else {
413                         return "";
414                 }
415         }
416
417         /**
418          * Looks for the function's reference page inside the doc.zip file and
419          * returns a filtered HTML source of it embedded in the template
420          * 
421          * @param funcName
422          *            Function name
423          * @return HTML source of reference page
424          */
425         public String getHtmlSource(String funcName) {
426                 Bundle bundle = Platform.getBundle(PHPHelpPlugin.PLUGIN_ID);
427                 URL fileURL = FileLocator.find(bundle, docPath, null);
428                 ZipEntry entry;
429                 byte[] b = null;
430                 try {
431                         URL resolve = FileLocator.resolve(fileURL);
432                         ZipFile docFile = new ZipFile(resolve.getPath());
433                         entry = docFile.getEntry("doc/function."+funcName.replace('_', '-')+".html");
434                         if(entry == null){
435                                 entry = docFile.getEntry("doc/ini."+funcName.replace('_', '-')+".html");
436                         }
437                         InputStream ref = docFile.getInputStream(entry);
438                         b = new byte[(int)entry.getSize()];
439                         ref.read(b, 0, (int)entry.getSize());
440                         if (b != null) {
441                                 String reference = filterHtmlSource(new String(b));
442                                 String refPageTpl = getRefPageTemplate();
443                                 refPageTpl = refPageTpl.replaceAll("%title%", funcName);
444                                 refPageTpl = replace(refPageTpl, "%reference%", reference);
445                                 return refPageTpl;
446                         }
447                 } catch (IOException e) {
448                         return "<html></html>";
449                 } catch (Exception e) {
450                         return null;
451                 }
452                 return "<html></html>";
453         }
454
455         /**
456          * Looks for the function's reference page inside the doc.zip file and
457          * returns a filtered HTML source of it embedded in the template
458          * 
459          * @param funcName
460          *            Function name
461          * @return HTML source of reference page
462          */
463         public String getLinkHtmlSource(String funcName) {
464                 Bundle bundle = Platform.getBundle(PHPHelpPlugin.PLUGIN_ID);
465                 URL fileURL = FileLocator.find(bundle, docPath, null);
466                 ZipEntry entry;
467                 byte[] b = null;
468                 try {
469                         URL resolve = FileLocator.resolve(fileURL);
470                         ZipFile docFile = new ZipFile(resolve.getPath());
471                         entry = docFile.getEntry("doc/"+funcName);
472                         InputStream ref = docFile.getInputStream(entry);
473                         b = new byte[(int)entry.getSize()];
474                         ref.read(b, 0, (int)entry.getSize());
475                         if (b != null) {
476                                 String reference = null;
477                                 String aFuncName = funcName.toString();
478                                 if(aFuncName.startsWith("function")){
479                                         reference = filterHtmlSource(new String(b));
480                                 } else if (aFuncName.startsWith("ini")){
481                                         reference = filterIniHtmlSource(new String(b));
482                                 } else if (aFuncName.startsWith("install")){
483                                         reference = filterIniHtmlSource(new String(b));
484                                 } else if (aFuncName.startsWith("language")){
485                                         reference = filterLangHtmlSource(new String(b));
486                                 } else if (aFuncName.startsWith("ref")){
487                                         reference = filterRefHtmlSource(new String(b));
488                                 }
489                                 String refPageTpl = getRefPageTemplate();
490                                 refPageTpl = refPageTpl.replaceAll("%title%", funcName);
491                                 refPageTpl = replace(refPageTpl, "%reference%", reference);
492                                 return refPageTpl;
493                         }
494                 } catch (IOException e) {
495                         return "<html></html>";
496                 } catch (Exception e) {
497                         return null;
498                 }
499                 return "<html></html>";
500         }
501         /**
502          * Returns the currently active java editor, or <code>null</code> if it
503          * cannot be determined.
504          * 
505          * @return the currently active java editor, or <code>null</code>
506          */
507         private PHPEditor getJavaEditor() {
508                 try {
509                         IEditorPart part = PHPeclipsePlugin.getActivePage().getActiveEditor();
510                         if (part instanceof PHPEditor)
511                                 return (PHPEditor) part;
512                         else
513                                 return null;
514                 } catch (Exception e) {
515                         return null;
516                 }
517         }
518
519 }