Merged xdebug from 1.3.x
[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                                 } else if (loc.startsWith("jar:")) {
114                                         // TODO find a better way of not showing the location error page. This is a cheap trick
115                                         // to keep the page from showing.
116                                         // ed_mann
117                                         browser.setText("<html></html>");
118                                 }
119                         }
120                 });
121                 parent.pack();
122                 if ((lastEditor = getJavaEditor()) != null) {
123                         SelectionListenerWithASTManager.getDefault().addListener(lastEditor, this);
124                 }
125                 getSite().getWorkbenchWindow().getSelectionService()
126                                 .addPostSelectionListener(PHPeclipsePlugin.EDITOR_ID, this);
127         }
128
129         /**
130          * Cleanup to remove the selection listener
131          */
132         public void dispose() {
133                 getSite().getWorkbenchWindow().getSelectionService()
134                                 .removePostSelectionListener(PHPeclipsePlugin.EDITOR_ID, this);
135         }
136
137         /**
138          * Passing the focus request to the viewer's control.
139          */
140         public void setFocus() {
141                 browser.setFocus();
142         }
143
144         /**
145          * Treats selection changes from the PHPEditor
146          */
147         public void selectionChanged(IEditorPart part, ITextSelection selection) {
148                 IDocument document = ((PHPEditor)part).getViewer().getDocument();
149                 int offset = selection.getOffset();
150                 IRegion iRegion = JavaWordFinder.findWord(document, offset);
151                 if (document != null && iRegion != null) {
152                         try {
153                                 final String wordStr = document.get(iRegion.getOffset(),
154                                                 iRegion.getLength());
155                                 if (!wordStr.equalsIgnoreCase(lastOccurrence)) {
156                                         showReference(wordStr);                         
157                                         lastOccurrence = wordStr;
158                                 }
159                         } catch (Exception e) {
160                                 e.printStackTrace();
161                         }
162                 }
163         }
164
165         /**
166          * Treats selection changes from the workbench. When part is new
167          * instance of PHPEditor it gets a listener attached
168          */
169         public void selectionChanged(IWorkbenchPart part, ISelection selection) {
170                 if (part != null && !((PHPEditor)part).equals(lastEditor)) {
171                         SelectionListenerWithASTManager.getDefault().addListener((PHPEditor)part, this);
172                         lastEditor = (PHPEditor)part;
173                 } else {
174                         System.out.println(part);
175                 }
176         }
177
178         /**
179          * Updates the browser with the reference page for a given function
180          * 
181          * @param funcName Function name
182          */
183         private void showReference(final String funcName) {
184                 new Thread(new Runnable() {
185                         public void run() {
186                                 Display.getDefault().asyncExec(new Runnable() {
187                                         public void run() {
188                                                 String html = getHtmlSource(funcName);
189                                                 browser.setText(html);
190                                         }
191                                 });
192                         }
193                 }).start();
194         }
195         
196         /**
197          * Updates the browser with the reference page for a given function
198          * 
199          * @param funcName Function name
200          */
201         private void showLinkReference(final String funcName) {
202                 new Thread(new Runnable() {
203                         public void run() {
204                                 Display.getDefault().asyncExec(new Runnable() {
205                                         public void run() {
206                                                 String html = getLinkHtmlSource(funcName);
207                                                 browser.setText(html);
208                                         }
209                                 });
210                         }
211                 }).start();
212         }
213
214         /**
215          * Filters the function's reference page extracting only parts of it
216          * 
217          * @param source HTML source of the reference page
218          * @return HTML source of reference page
219          */
220         private String filterIniHtmlSource(String source) {
221                 try {
222                         Parser parser = new Parser(source);
223                         String [] tagsToBeFound = {"DIV"};
224                         ArrayList classList = new ArrayList(8);
225                         classList.add("section");
226                         classList.add("title");
227                         classList.add("refsect1 parameters");
228                         classList.add("refsect1 returnvalues");
229                         classList.add("refsect1 examples");
230                         classList.add("refsect1 seealso");
231                         classList.add("refsect1 u");
232                         TagFindingVisitor visitor = new TagFindingVisitor(tagsToBeFound);
233                         parser.visitAllNodesWith(visitor);
234                         Node [] allPTags = visitor.getTags(0);
235                         StringBuffer output = new StringBuffer();
236                         for (int i = 0; i < allPTags.length; i++) {
237                                 String tagClass = ((Div)allPTags[i]).getAttribute("class");
238                                 if (classList.contains(tagClass)) {
239                                         output.append(allPTags[i].toHtml());
240                                 }
241                         }
242                         return output.toString().replaceAll("—", "-");
243                         //.replace("<h3 class=\"title\">Description</h3>", " ");
244                 } catch (ParserException e) {
245                         e.printStackTrace();
246                 }
247                 return "";
248         }
249         
250         /**
251          * Filters the function's reference page extracting only parts of it
252          * 
253          * @param source HTML source of the reference page
254          * @return HTML source of reference page
255          */
256         private String filterLangHtmlSource(String source) {
257                 try {
258                         Parser parser = new Parser(source);
259                         String [] tagsToBeFound = {"DIV"};
260                         ArrayList classList = new ArrayList(8);
261                         classList.add("sect1");
262                         classList.add("title");
263                         classList.add("refsect1 parameters");
264                         classList.add("refsect1 returnvalues");
265                         classList.add("refsect1 examples");
266                         classList.add("refsect1 seealso");
267                         classList.add("refsect1 u");
268                         TagFindingVisitor visitor = new TagFindingVisitor(tagsToBeFound);
269                         parser.visitAllNodesWith(visitor);
270                         Node [] allPTags = visitor.getTags(0);
271                         StringBuffer output = new StringBuffer();
272                         for (int i = 0; i < allPTags.length; i++) {
273                                 String tagClass = ((Div)allPTags[i]).getAttribute("class");
274                                 if (classList.contains(tagClass)) {
275                                         output.append(allPTags[i].toHtml());
276                                 }
277                         }
278                         return output.toString().replaceAll("—", "-");
279                         //.replace("<h3 class=\"title\">Description</h3>", " ");
280                 } catch (ParserException e) {
281                         e.printStackTrace();
282                 }
283                 return "";
284         }
285         
286         /**
287          * Filters the function's reference page extracting only parts of it
288          * 
289          * @param source HTML source of the reference page
290          * @return HTML source of reference page
291          */
292         private String filterRefHtmlSource(String source) {
293                 try {
294                         Parser parser = new Parser(source);
295                         String [] tagsToBeFound = {"DIV"};
296                         ArrayList classList = new ArrayList(8);
297                         classList.add("partintro");
298                         classList.add("section");
299                         classList.add("title");
300                         classList.add("refsect1 parameters");
301                         classList.add("refsect1 returnvalues");
302                         classList.add("refsect1 examples");
303                         classList.add("refsect1 seealso");
304                         classList.add("refsect1 u");
305                         TagFindingVisitor visitor = new TagFindingVisitor(tagsToBeFound);
306                         parser.visitAllNodesWith(visitor);
307                         Node [] allPTags = visitor.getTags(0);
308                         StringBuffer output = new StringBuffer();
309                         for (int i = 0; i < allPTags.length; i++) {
310                                 String tagClass = ((Div)allPTags[i]).getAttribute("class");
311                                 if (classList.contains(tagClass)) {
312                                         output.append(allPTags[i].toHtml());
313                                 }
314                         }
315                         return output.toString().replaceAll("—", "-");
316                         //.replace("<h3 class=\"title\">Description</h3>", " ");
317                 } catch (ParserException e) {
318                         e.printStackTrace();
319                 }
320                 return "";
321         }
322         
323         /**
324          * Filters the function's reference page extracting only parts of it
325          * 
326          * @param source HTML source of the reference page
327          * @return HTML source of reference page
328          */
329         private String filterHtmlSource(String source) {
330                 try {
331                         Parser parser = new Parser(source);
332                         String [] tagsToBeFound = {"DIV"};
333                         ArrayList classList = new ArrayList(8);
334                         classList.add("refnamediv");
335                         classList.add("refsect1 description");
336                         classList.add("refsect1 parameters");
337                         classList.add("refsect1 returnvalues");
338                         classList.add("refsect1 examples");
339                         classList.add("refsect1 seealso");
340                         classList.add("refsect1 u");
341                         TagFindingVisitor visitor = new TagFindingVisitor(tagsToBeFound);
342                         parser.visitAllNodesWith(visitor);
343                         Node [] allPTags = visitor.getTags(0);
344                         StringBuffer output = new StringBuffer();
345                         for (int i = 0; i < allPTags.length; i++) {
346                                 String tagClass = ((Div)allPTags[i]).getAttribute("class");
347                                 if (classList.contains(tagClass)) {
348                                         output.append(allPTags[i].toHtml());
349                                 }
350                         }
351                         return output.toString().replaceAll("—", "-");
352                         //.replace("<h3 class=\"title\">Description</h3>", " ");
353                 } catch (ParserException e) {
354                         e.printStackTrace();
355                 }
356                 return "";
357         }
358         /**
359          * Reads the template that defines the style of the reference page
360          * shown inside the view's browser
361          * 
362          * @return HTML source of the template
363          */
364         public String getRefPageTemplate() {
365                 Bundle bundle = Platform.getBundle(PHPManualUIPlugin.PLUGIN_ID);
366                 URL fileURL = FileLocator.find(bundle, new Path("templates"), null);
367                 StringBuffer contents = new StringBuffer();
368                 BufferedReader input = null;
369                 try {
370                         URL resolve = FileLocator.resolve(fileURL);
371                         input = new BufferedReader(new FileReader(resolve.getPath()+"/refpage.html"));
372                         String line = null;
373                         while ((line = input.readLine()) != null){
374                                 contents.append(line);
375                         }
376                 }
377                 catch (FileNotFoundException e) {
378                         e.printStackTrace();
379                 } catch (IOException e) {
380                         e.printStackTrace();
381                 }
382                 finally {
383                         try {
384                                 if (input!= null) {
385                                         input.close();
386                                 }
387                         }
388                         catch (IOException ex) {
389                                 ex.printStackTrace();
390                         }
391                 }
392                 return contents.toString();
393         }
394
395         /**
396          * Replaces each substring of source string that matches the
397          * given pattern string with the given replace string
398          * 
399          * @param source The source string
400          * @param pattern The pattern string
401          * @param replace The replace string
402          * @return The resulting String
403          */
404         public static String replace(String source, String pattern, String replace) {
405                 if (source != null) {
406                         final int len = pattern.length();
407                         StringBuffer sb = new StringBuffer();
408                         int found = -1;
409                         int start = 0;
410                         while ((found = source.indexOf(pattern, start)) != -1) {
411                                 sb.append(source.substring(start, found));
412                                 sb.append(replace);
413                                 start = found + len;
414                         }
415                         sb.append(source.substring(start));
416                         return sb.toString();
417                 } else {
418                         return "";
419                 }
420         }
421
422         /**
423          * Looks for the function's reference page inside the doc.zip file and
424          * returns a filtered HTML source of it embedded in the template
425          * 
426          * @param funcName
427          *            Function name
428          * @return HTML source of reference page
429          */
430         public String getHtmlSource(String funcName) {
431                 Bundle bundle = Platform.getBundle(PHPHelpPlugin.PLUGIN_ID);
432                 URL fileURL = FileLocator.find(bundle, docPath, null);
433                 ZipEntry entry;
434                 byte[] b = null;
435                 try {
436                         URL resolve = FileLocator.resolve(fileURL);
437                         ZipFile docFile = new ZipFile(resolve.getPath());
438                         entry = docFile.getEntry("doc/function."+funcName.replace('_', '-')+".html");
439                         if(entry == null){
440                                 entry = docFile.getEntry("doc/ini."+funcName.replace('_', '-')+".html");
441                         }
442                         InputStream ref = docFile.getInputStream(entry);
443                         b = new byte[(int)entry.getSize()];
444                         ref.read(b, 0, (int)entry.getSize());
445                         if (b != null) {
446                                 String reference = filterHtmlSource(new String(b));
447                                 String refPageTpl = getRefPageTemplate();
448                                 refPageTpl = refPageTpl.replaceAll("%title%", funcName);
449                                 refPageTpl = replace(refPageTpl, "%reference%", reference);
450                                 return refPageTpl;
451                         }
452                 } catch (IOException e) {
453                         return "<html></html>";
454                 } catch (Exception e) {
455                         return null;
456                 }
457                 return "<html></html>";
458         }
459
460         /**
461          * Looks for the function's reference page inside the doc.zip file and
462          * returns a filtered HTML source of it embedded in the template
463          * 
464          * @param funcName
465          *            Function name
466          * @return HTML source of reference page
467          */
468         public String getLinkHtmlSource(String funcName) {
469                 Bundle bundle = Platform.getBundle(PHPHelpPlugin.PLUGIN_ID);
470                 URL fileURL = FileLocator.find(bundle, docPath, null);
471                 ZipEntry entry;
472                 byte[] b = null;
473                 try {
474                         URL resolve = FileLocator.resolve(fileURL);
475                         ZipFile docFile = new ZipFile(resolve.getPath());
476                         entry = docFile.getEntry("doc/"+funcName);
477                         InputStream ref = docFile.getInputStream(entry);
478                         b = new byte[(int)entry.getSize()];
479                         ref.read(b, 0, (int)entry.getSize());
480                         if (b != null) {
481                                 String reference = null;
482                                 String aFuncName = funcName.toString();
483                                 if(aFuncName.startsWith("function")){
484                                         reference = filterHtmlSource(new String(b));
485                                 } else if (aFuncName.startsWith("ini")){
486                                         reference = filterIniHtmlSource(new String(b));
487                                 } else if (aFuncName.startsWith("install")){
488                                         reference = filterIniHtmlSource(new String(b));
489                                 } else if (aFuncName.startsWith("language")){
490                                         reference = filterLangHtmlSource(new String(b));
491                                 } else if (aFuncName.startsWith("ref")){
492                                         reference = filterRefHtmlSource(new String(b));
493                                 }
494                                 String refPageTpl = getRefPageTemplate();
495                                 refPageTpl = refPageTpl.replaceAll("%title%", funcName);
496                                 refPageTpl = replace(refPageTpl, "%reference%", reference);
497                                 return refPageTpl;
498                         }
499                 } catch (IOException e) {
500                         return "<html></html>";
501                 } catch (Exception e) {
502                         return null;
503                 }
504                 return "<html></html>";
505         }
506         /**
507          * Returns the currently active java editor, or <code>null</code> if it
508          * cannot be determined.
509          * 
510          * @return the currently active java editor, or <code>null</code>
511          */
512         private PHPEditor getJavaEditor() {
513                 try {
514                         IEditorPart part = PHPeclipsePlugin.getActivePage().getActiveEditor();
515                         if (part instanceof PHPEditor)
516                                 return (PHPEditor) part;
517                         else
518                                 return null;
519                 } catch (Exception e) {
520                         return null;
521                 }
522         }
523
524 }