View Javadoc

1   /*
2    * Copyright 2011 Andres Gomez Casanova
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.mgaip;
17  
18  import java.io.File;
19  import java.io.FileInputStream;
20  import java.io.FileOutputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStreamWriter;
24  import java.io.Writer;
25  
26  import org.jdom.Comment;
27  import org.jdom.Document;
28  import org.jdom.Element;
29  import org.jdom.JDOMException;
30  import org.jdom.Namespace;
31  import org.jdom.input.SAXBuilder;
32  import org.jdom.output.Format;
33  import org.jdom.output.XMLOutputter;
34  import org.jdom.xpath.XPath;
35  
36  /**
37   * Class that replaces the already existent reports.
38   * <p>
39   * <b>Control Version</b>
40   * <p>
41   * <ul>
42   * <li>1.0 Class creation.</li>
43   * </ul>
44   *
45   * @author Jose Francisco "Pachito" Saray
46   * @author Andres Gomez Casanova <a
47   *         href="mailto:a n g o c a at y a h o o dot c o m">(AngocA)</a>
48   * @version 1.0 20110529
49   * @since 1.0
50   */
51  public final class Replacer {
52  
53      /**
54       * Internal id for horizontal ad.
55       */
56      private static final int HORIZONTAL_AD = 2;
57      /**
58       * Internal id for logo ad.
59       */
60      private static final int LOGO_AD = 4;
61      /**
62       * Internal id for unique ad.
63       */
64      private static final int UNIQUE_AD = 3;
65      /**
66       * Internal id for vertical ad.
67       */
68      private static final int VERTICAL_AD = 1;
69  
70      /**
71       * Mojo that calls the replacer.
72       */
73      private AdInserterMojo report;
74  
75      /**
76       * Instantiates the mojo.
77       *
78       * @param caller
79       *            Mojo containing all parameters.
80       */
81      Replacer(final AdInserterMojo/* ! */caller) {
82          this.report = caller;
83      }
84  
85      /**
86       * Replaces an HTML tag with the given type of ad.
87       *
88       * @param single
89       *            XML component that contains the location of the tag to
90       *            replace.
91       * @param content
92       *            Content to insert.
93       * @return true because the HTML has been modified and it has to be
94       *         rewritten.
95       */
96      private boolean changeHTMLTag(final Element/* ! */single,
97              final String/* ! */content) {
98          Element parelement = single.getParentElement();
99          single.detach();
100         single.removeChild("img");
101         Element script = new Element("script");
102         script.setAttribute("type", "text/javascript");
103         Comment comment = new Comment(content);
104         script.addContent(comment);
105         parelement.addContent(script);
106         Element common = this.getCommon();
107         parelement.addContent(common);
108         return true;
109     }
110 
111     /**
112      * Returns the Google ad according to the requested type.
113      *
114      * @param type
115      *            Type of ad.
116      * @return Script to show this ad in html.
117      */
118     private String/* ! */getAdContent(final int type) {
119         // assert type >= 1 && type <=4 : "Unknown ad type";
120 
121         String ret = "\n";
122         ret += "google_ad_client = \"" + report.googleId + "\";\n";
123         switch (type) {
124         case HORIZONTAL_AD:
125             ret += "google_ad_slot = \"" + report.horizontalAdId + "\";\n";
126             ret += "google_ad_width = " + report.horizontalAdWidth + ";\n";
127             ret += "google_ad_height = " + report.horizontalAdHeight + ";\n";
128             break;
129         case LOGO_AD:
130             ret += "google_ad_slot = \"" + report.logoAdId + "\";\n";
131             ret += "google_ad_width = " + report.logoAdWidth + ";\n";
132             ret += "google_ad_height = " + report.logoAdHeight + ";\n";
133             break;
134         case UNIQUE_AD:
135             ret += "google_ad_slot = \"" + report.uniqueAdId + "\";\n";
136             ret += "google_ad_width = " + report.uniqueAdWidth + ";\n";
137             ret += "google_ad_height = " + report.uniqueAdHeight + ";\n";
138             break;
139         case VERTICAL_AD:
140             ret += "google_ad_slot = \"" + report.verticalAdId + "\";\n";
141             ret += "google_ad_width = " + report.verticalAdWidth + ";\n";
142             ret += "google_ad_height = " + report.verticalAdHeight + ";\n";
143             break;
144         default:
145             this.report.log(2, "No content assigned.");
146             break;
147         }
148         ret += "//";
149 
150         return ret;
151     }
152 
153     /**
154      * @return Returns the common part of Google Ad.
155      */
156     private Element/* ! */getCommon() {
157         Element common = new Element("script");
158         common.setAttribute("type", "text/javascript");
159         // TODO Delete the namespace from the tag.
160         common.setAttribute("src",
161                 "http://pagead2.googlesyndication.com/pagead/show_ads.js");
162         common.setText("mgaip");
163         return common;
164     }
165 
166     /**
167      * Replaces the customPoweredBy tag with the given code.
168      * <p>
169      * TODO this should call an external javascript file that will contain the
170      * code and stuff to show here.
171      *
172      * @param single
173      *            Element where the custom code has to be inserted.
174      * @return true to indicate that the file has been modified..
175      */
176     private boolean/* ! */getCustomPoweredBy(final Element/* ! */single) {
177         Element parelement = single.getParentElement();
178         single.detach();
179         single.removeChild("img");
180         Element custom = new Element("div");
181         // TODO the next line should be inserted as raw, not as text.
182         custom.addContent(report.customPoweredBy);
183         parelement.addContent(custom);
184         return true;
185     }
186 
187     /**
188      * Returns the I like Facebook script.
189      * <p>
190      * <code>
191      * &lt;iframe src=
192      * "http://www.facebook.com/plugins/like.php?
193      * href=https%3A%2F%2Fwww.facebook.com%2Fpages%2FZemucan%2F138284808329
194      * &amp;send=false&amp;layout=box_count&amp;width=150&amp;show_faces=false
195      * &amp;action=like&amp;colorscheme=light&amp;font&amp;height=90"
196      * scrolling="no" frameborder="0"
197      * style="border:none; overflow:hidden; width:150px; height:90px;"
198      * allowTransparency="true"&gt;&lt;/iframe&gt;
199      * </code>
200      * <p>
201      * The example is for Zemucan, but it is defined in the variable
202      * facebookLink. There are two types of links:
203      * <ul>
204      * <li>
205      * In pages, with number:
206      * https://www.facebook.com/pages/IBM-InfoSphere-Warehouse/17628877758</li>
207      * <li>
208      * Direct: https://www.facebook.com/PHP</li>
209      * </ul>
210      * <p>
211      * However the link has to be transformed from
212      * <p>
213      * <code>https://www.facebook.com/pages/Zemucan/138284808329
214      * </code>
215      * <p>
216      * to
217      * <p>
218      * <code>https%3A%2F%2Fwww.facebook.com%2Fpages%2FZemucan%2F138284808329
219      * </code>
220      *
221      * @param single
222      *            Element where the code has to be inserted.
223      * @return true to indicate that the file has been modified.
224      */
225     private boolean/* ! */getFacebookILike(final Element/* ! */single) {
226         String src = "http://www.facebook.com/plugins/like.php";
227         src += "?href=" + report.facebookLink + "&";
228         src += "send=false&layout=box_count&width=150&";
229         src += "show_faces=false&action=like&colorscheme=light&";
230         src += "font&height=90";
231         String style = "border:none; overflow:hidden; width:150px; "
232                 + "height:90px;";
233 
234         Element parelement = single.getParentElement();
235         single.detach();
236         single.removeChild("img");
237         Element iframe = new Element("iframe");
238         iframe.setAttribute("src", src);
239         iframe.setAttribute("scrolling", "no");
240         iframe.setAttribute("frameborder", "0");
241         iframe.setAttribute("style", style);
242         iframe.setAttribute("allowTransparency", "true");
243         iframe.setText("mgaip");
244         parelement.addContent(iframe);
245         return true;
246     }
247 
248     /**
249      * Adds the google plus one script in the page.
250      *
251      * @param Element
252      *            where the code has to be inserted.
253      * @return true to indicate that the file has been modified.
254      */
255     private boolean getGooglePlusOne(final Element /* ! */single) {
256         Element parelement = single.getParentElement();
257         single.detach();
258         single.removeChild("img");
259         Element plusone = new Element("plusone", "g",
260                 "http://www.w3.org/1999/xhtml");
261         parelement.addContent(plusone);
262         plusone.setText("mgaip");
263         return true;
264     }
265 
266     /**
267      * Searches the tags to modify the HTML file and call the respective method
268      * to insert an ad.
269      * <p>
270      * Vertical (xpath: html/body/div id="leftColumn"/div id="navcolumn"
271      * title="googleAds"):
272      * <p>
273      * <code>
274      * &lt;a href="" title="googleAds" class="poweredBy"&gt;<br>
275      * &lt;logo name="googleAds" img="googleAds"/&gt;<br>
276      * &lt;/a&gt;
277      * </code>
278      * <p>
279      * Horizontal (xpath: html/body/div id="bodyColumn"/div id="contextBox"/div
280      * class="section"):
281      * <p>
282      * <code>
283      * &lt;img src="googleAds" alt="googleAds" /&gt;
284      * </code>
285      * <p>
286      * Unique (xpath: html/body/div id="bodyColumn"/div id="contextBox"/div
287      * class="section"/p):
288      * <p>
289      * <code>
290      * &lt;a href="#googleAds"&gt;googleAds&lt;/a&gt;
291      * </code>
292      * <p>
293      * Logo (xpath: html/body/div id="banner"/span id="bannerLeft" |
294      * id="bannerRight"):
295      * <p>
296      * <code>
297      * &lt;img src="googleAds" alt="googleAds" /&gt;
298      * </code>
299      * <p>
300      * And then put the publicity:
301      * <p>
302      * <code>
303      * &lt;script type="text/javascript"&gt;&lt;!--<br>
304      * google_ad_client = "XXXXX";<br>
305      * google_ad_slot = "YYYYY";<br>
306      * google_ad_width = 120;<br>
307      * google_ad_height = 600;<br>
308      * //--&gt;<br>
309      * &lt;/script&gt;<br>
310      * &lt;script type="text/javascript"<br>
311      * src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;<br>
312      * &lt;/script&gt;
313      * </code>
314      * <p>
315      * Where XXXXX the google user id, and YYYYY is the ad identifier.
316      * <p>
317      * Also, it replaces the powered by for facebook (xpath: html/body/div
318      * id="leftColumn"/div id="navcolumn" title="facebookILike").
319      * <p>
320      * <code>
321      * &lt;a href="" title="facebookILike" class="poweredBy"&gt;<br>
322      * &lt;logo name="facebookILike" img="facebookILike"/&gt;<br>
323      * &lt;/a&gt;
324      * </code>
325      * <p>
326      * And a custom code as powered by (xpath: html/body/div id="leftColumn"/div
327      * id="navcolumn" title="customPoweredBy").
328      * <p>
329      * <code>
330      * &lt;a href="" title="customPoweredBy" class="poweredBy"&gt;<br>
331      * &lt;logo name="customPoweredBy" img="customPoweredBy"/&gt;<br>
332      * &lt;/a&gt;
333      * </code>
334      * <p>
335      *
336      * @param elem
337      *            Root element.
338      * @return true if the HTML has been modified and it has to be rewritten,
339      *         false otherwise.
340      * @throws JDOMException
341      *             If there is a problem
342      */
343     private boolean htmlReplacement(final Element/* ! */elem)
344             throws JDOMException {
345         boolean modified = false;
346 
347         Namespace xhtml = Namespace.getNamespace("xhtml",
348                 "http://www.w3.org/1999/xhtml");
349 
350         // FIXME when there are multiple powered by, there is a problem in the
351         // order of insertion.
352 
353         // PoweredBy
354         XPath xpath = XPath.newInstance("//xhtml:div[@id='leftColumn']"
355                 + "//xhtml:div[@id='navcolumn']//xhtml:a[@class='poweredBy' "
356                 + "and @title='googleAds']");
357         xpath.addNamespace(xhtml);
358         Element single = (Element) xpath.selectSingleNode(elem);
359         if (single != null) {
360             this.report.log(1, "Powered - Vertical");
361             modified = this.changeHTMLTag(single,
362                     this.getAdContent(VERTICAL_AD));
363         }
364 
365         // Horizontal
366         xpath = XPath.newInstance("//xhtml:div[@id='bodyColumn']"
367                 + "/xhtml:div[@id='contentBox']/xhtml:div[@class='section']"
368                 + "/xhtml:img[@src='googleAds' and @alt='googleAds']");
369         xpath.addNamespace(xhtml);
370         single = (Element) xpath.selectSingleNode(elem);
371         if (single != null) {
372             this.report.log(1, "Horizontal");
373             modified = this.changeHTMLTag(single,
374                     this.getAdContent(HORIZONTAL_AD));
375         }
376 
377         // Unique
378         xpath = XPath.newInstance("//xhtml:div[@id='bodyColumn']"
379                 + "/xhtml:div[@id='contentBox']"
380                 + "/xhtml:div[@class='section']/xhtml:p"
381                 + "/xhtml:a[@href='#googleAds']");
382         xpath.addNamespace(xhtml);
383         single = (Element) xpath.selectSingleNode(elem);
384         if (single != null) {
385             this.report.log(1, "Unique");
386             modified = this.changeHTMLTag(single, this.getAdContent(UNIQUE_AD));
387         }
388 
389         // Logo
390         xpath = XPath.newInstance("//xhtml:div[@id='banner']"
391                 + "/xhtml:div[@id='bannerRight']"
392                 + "/xhtml:img[@src='googleAds' and @alt='googleAds']");
393         xpath.addNamespace(xhtml);
394         single = (Element) xpath.selectSingleNode(elem);
395         if (single != null) {
396             this.report.log(1, "Logo right");
397             modified = this.changeHTMLTag(single, this.getAdContent(LOGO_AD));
398         }
399         xpath = XPath.newInstance("//xhtml:div[@id='banner']"
400                 + "/xhtml:div[@id='bannerLeft']"
401                 + "/xhtml:img[@src='googleAds' and @alt='googleAds']");
402         xpath.addNamespace(xhtml);
403         single = (Element) xpath.selectSingleNode(elem);
404         if (single != null) {
405             this.report.log(1, "Logo left");
406             modified = this.changeHTMLTag(single, this.getAdContent(LOGO_AD));
407         }
408 
409         // Facebook
410         xpath = XPath.newInstance("//xhtml:div[@id='leftColumn']"
411                 + "/xhtml:div[@id='navcolumn']/xhtml:a[@class='poweredBy' "
412                 + "and @title='facebookILike']");
413         xpath.addNamespace(xhtml);
414         single = (Element) xpath.selectSingleNode(elem);
415         if (single != null) {
416             this.report.log(1, "Powered - Facebook");
417             modified = this.getFacebookILike(single);
418         }
419 
420         // Plusone
421         xpath = XPath.newInstance("//xhtml:div[@id='leftColumn']"
422                 + "/xhtml:div[@id='navcolumn']/xhtml:a[@class='poweredBy' "
423                 + "and @title='googlePlusOne']");
424         xpath.addNamespace(xhtml);
425         single = (Element) xpath.selectSingleNode(elem);
426         if (single != null) {
427             this.report.log(1, "Powered - GooglePlusOne");
428             modified = this.getGooglePlusOne(single);
429         }
430 
431         // Custom
432         xpath = XPath.newInstance("//xhtml:div[@id='leftColumn']"
433                 + "/xhtml:div[@id='navcolumn']/xhtml:a[@class='poweredBy' "
434                 + "and @title='customPoweredBy']");
435         xpath.addNamespace(xhtml);
436         single = (Element) xpath.selectSingleNode(elem);
437         if (single != null) {
438             this.report.log(1, "Powered - Custom");
439             modified = this.getCustomPoweredBy(single);
440         }
441 
442         return modified;
443     }
444 
445     /**
446      * Open the file, parses the HTML and the searches the defined tags to
447      * replace with other content.
448      *
449      * @param directory
450      *            Directory of the file.
451      * @param filename
452      *            Name of the file to modify.
453      */
454     protected void replace(final String/* ! */directory,
455             final String/* ! */filename) {
456         // assert filename != null && !filename.equals("") : "Invalid filename";
457         this.report.log(2, "Processing file " + filename);
458         FileOutputStream output = null;
459         Writer out = null;
460 
461         SAXBuilder saxb = new SAXBuilder();
462         saxb.setValidation(false);
463         saxb.setEntityResolver(new IgnoreEntityResolver());
464 
465         try {
466             InputStream file = new FileInputStream(directory
467                     + File.separatorChar + filename);
468             Document document = saxb.build(file);
469             Element elem = document.getRootElement();
470             boolean modified = this.htmlReplacement(elem);
471 
472             // Writes the new html modified.
473             if (modified) {
474                 this.report.log(1, "HTML re-written");
475                 XMLOutputter xmloutputter = new XMLOutputter(
476                         Format.getPrettyFormat());
477                 output = new FileOutputStream(directory + File.separatorChar
478                         + filename);
479                 out = new OutputStreamWriter(output, "ISO-8859-1");
480                 xmloutputter.output(document.getDocType(), output);
481                 output.write('\n');
482                 xmloutputter.output(elem, output);
483             }
484         } catch (Throwable e) {
485             this.report.log(4, "General problem: " + e.getMessage());
486             this.report.log(e);
487         } finally {
488             if (out != null) {
489                 try {
490                     out.close();
491                 } catch (IOException e) {
492                     this.report.log(4, "IO Exception: " + e.getMessage());
493                     this.report.log(e);
494                 }
495             }
496             if (output != null) {
497                 try {
498                     output.flush();
499                     output.close();
500                 } catch (IOException e) {
501                     this.report.log(4, "IO Exception: " + e.getMessage());
502                     this.report.log(e);
503                 }
504             }
505         }
506     }
507 }