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