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 * <iframe src=
192 * "http://www.facebook.com/plugins/like.php?
193 * href=https%3A%2F%2Fwww.facebook.com%2Fpages%2FZemucan%2F138284808329
194 * &send=false&layout=box_count&width=150&show_faces=false
195 * &action=like&colorscheme=light&font&height=90"
196 * scrolling="no" frameborder="0"
197 * style="border:none; overflow:hidden; width:150px; height:90px;"
198 * allowTransparency="true"></iframe>
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 * <a href="" title="googleAds" class="poweredBy"><br>
275 * <logo name="googleAds" img="googleAds"/><br>
276 * </a>
277 * </code>
278 * <p>
279 * Horizontal (xpath: html/body/div id="bodyColumn"/div id="contextBox"/div
280 * class="section"):
281 * <p>
282 * <code>
283 * <img src="googleAds" alt="googleAds" />
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 * <a href="#googleAds">googleAds</a>
291 * </code>
292 * <p>
293 * Logo (xpath: html/body/div id="banner"/span id="bannerLeft" |
294 * id="bannerRight"):
295 * <p>
296 * <code>
297 * <img src="googleAds" alt="googleAds" />
298 * </code>
299 * <p>
300 * And then put the publicity:
301 * <p>
302 * <code>
303 * <script type="text/javascript"><!--<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 * //--><br>
309 * </script><br>
310 * <script type="text/javascript"<br>
311 * src="http://pagead2.googlesyndication.com/pagead/show_ads.js"><br>
312 * </script>
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 * <a href="" title="facebookILike" class="poweredBy"><br>
322 * <logo name="facebookILike" img="facebookILike"/><br>
323 * </a>
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 * <a href="" title="customPoweredBy" class="poweredBy"><br>
331 * <logo name="customPoweredBy" img="customPoweredBy"/><br>
332 * </a>
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 }