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