001 /** 002 * This package is DOMLET!!! 003 */ 004 package net.sf.jolene.dom; 005 006 import net.sf.jolene.constants.Prefs; 007 import net.sf.jolene.constants.Tags; 008 import net.sf.jolene.html.Attributes; 009 import net.sf.jolene.html.IAttributes; 010 import org.apache.log4j.LogManager; 011 import org.apache.log4j.Logger; 012 013 import java.io.*; 014 import java.util.*; 015 016 /** 017 * HTML Document object. 018 * 019 * @author Dan Howard 020 * @since Sep 21, 2003 5:22:33 PM 021 */ 022 public final class Document { 023 024 // todo modifiy file constructor to use File object - add constructor passing in the content of the document 025 026 private static final Logger log = LogManager.getLogger(Document.class); 027 028 // These are default protection so they are available to the Parser. The Parser's job is 029 // to initialize and define these objects. 030 String title; 031 List<Header> head; 032 String doctype; // Store the text before the start of HTML - usually !DOCTYPE 033 String bodyText; // Body text string. 034 035 List<Form> forms; 036 IAttributes body; 037 038 private String fileName; 039 private String contextPath; 040 private String uri; 041 042 /** 043 * Indicator for labels being translated. We do not do the translations here. 044 * We expect that the translation is done at the application level. This can 045 * therefore be used to decide if the document is already translated (so you 046 * don't need to do it over again. 047 * <p/> 048 * see net.sf.jolene.struts.DomletAction for the struts implementation 049 */ 050 private boolean translated; 051 052 /** 053 * @param fileName 054 * @param contextPath 055 * @param uri 056 * @throws IOException 057 * @throws MalformedHTMLException - runtime exception 058 */ 059 public Document(String fileName, String contextPath, String uri) throws IOException, MalformedHTMLException { 060 this(fileName); 061 this.contextPath = contextPath; 062 this.uri = uri; 063 translatePaths(); 064 } 065 066 /** 067 * @param fileName - file name of the document. 068 * @throws IOException - if an IOException occurs 069 * @throws MalformedHTMLException - runtime exception 070 */ 071 public Document(String fileName) throws IOException, MalformedHTMLException { 072 this(); 073 this.fileName = fileName; 074 open(); 075 } 076 077 /** 078 * For clone only. 079 */ 080 Document() { 081 title = ""; 082 head = new ArrayList<Header>(4); 083 doctype = ""; 084 bodyText = ""; 085 contextPath = ""; 086 uri = ""; 087 body = new Attributes(); 088 forms = new ArrayList<Form>(4); 089 uri = ""; 090 } 091 092 093 /** 094 * Adds a header element to the document. 095 * 096 * @param header a Header object. 097 * @return boolean to indicate success. 098 */ 099 public boolean addHeader(Header header) { 100 return head.add(header); 101 } 102 103 /** 104 * This is a custom clone implementation which ensures the document 105 * is cleanly and safely cloned. Used by the DocumentFactory. 106 * 107 * @return Document 108 */ 109 @Override 110 public Document clone() { 111 Document doc = new Document(); 112 doc.bodyText = bodyText; 113 doc.fileName = fileName; 114 doc.uri = uri; 115 doc.doctype = doctype; 116 doc.title = title; 117 118 Iterator<Header> ith; 119 ith = head.iterator(); 120 while (ith.hasNext()) { 121 doc.head.add(ith.next().clone()); 122 } 123 124 Iterator it; 125 doc.body = new Attributes(); 126 it = body.keySet().iterator(); 127 while (it.hasNext()) { 128 Object key; 129 Object value; 130 key = it.next(); 131 value = body.getAttribute(key.toString()); 132 doc.body.setAttribute(key.toString(), value.toString()); 133 } 134 135 doc.forms = new ArrayList<Form>(forms.size()); 136 for (int i = 0; i < forms.size(); i++) { 137 doc.forms.add(forms.get(i).clone()); 138 } 139 140 return doc; 141 } 142 143 /** 144 * Returns a object by searching all forms in the document. 145 * 146 * @param id The id of the element. 147 * @return HTMLElement 148 */ 149 public HTMLElement elements(String id) { 150 HTMLElement element = null; 151 152 if (forms != null && forms.size() > 0) { 153 for (int j = 0; j < forms.size(); j++) { 154 element = forms.get(j).elements(id); 155 if (element != null) { 156 return element; 157 } 158 } 159 } 160 161 return element; 162 } 163 164 /** 165 * Returns the form object specified by the number. 166 * 167 * @param index form number (starting with 0) 168 * @return Form a Form object 169 * @throws IndexOutOfBoundsException - if the index is out of range (index < 0 || index >= size()). 170 */ 171 public Form forms(int index) { 172 return forms.get(index); 173 } 174 175 /** 176 * Returns a form object specified by name. For the form to be found it will require a 'name' attribute. 177 * @param name name of the form in the document (case insensitive). 178 * @return Form a Form object or null if not found. 179 */ 180 public Form forms(String name) { 181 for (Form form: forms) { 182 if (name.equalsIgnoreCase(form.getName())) { 183 return form; 184 } 185 } 186 return null; 187 } 188 189 /** 190 * Retrives the body attributes. 191 * 192 * @return IAttributes 193 */ 194 public IAttributes getBody() { 195 return body; 196 } 197 198 /** 199 * Gets the doc type of the document. 200 * 201 * @return String 202 */ 203 public String getDoctype() { 204 return doctype; 205 } 206 207 208 /** 209 * @return Number of forms in the document. 210 */ 211 public int getFormCount() { 212 return forms.size(); 213 } 214 215 /** 216 * Returns the number of Header elements in the document. 217 * 218 * @return int number of Header elements. 219 */ 220 public int getHeaderCount() { 221 return head.size(); 222 } 223 224 /** 225 * Gets the document title. 226 * 227 * @return title 228 */ 229 public String getTitle() { 230 return title; 231 } 232 233 /** 234 * @return uri string for this document. 235 */ 236 public String getUri() { 237 return uri; 238 } 239 240 /** 241 * Return the Header element specified by the index based on document order. 242 * 243 * @param header to the Header (starting with 0). 244 * @return Header element. 245 */ 246 public Header headers(int header) { 247 return head.get(header); 248 } 249 250 /** 251 * Indicator for labels being translated. We do not do the translations here. 252 * We expect that the translation is done at the application level. This can 253 * therefore be used to decide if the document is already translated (so you 254 * don't need to do it over again. 255 * <p/> 256 * see net.sf.jolene.struts.DomletAction for the struts implementation 257 * 258 * @return true if this document is translated 259 */ 260 public boolean isTranslated() { 261 return translated; 262 } 263 264 /** 265 * Remove the headers 266 * @param index to the header in the document. Zero based in document order. 267 * @return The removed header object. 268 */ 269 public Header removeHeader(int index) { 270 return head.remove(index); 271 } 272 273 /** 274 * Sets the doc type of the document. 275 * 276 * @param doctype The HTML doctype string. 277 */ 278 public void setDoctype(String doctype) { 279 this.doctype = doctype; 280 } 281 282 /** 283 * Set the document title. 284 * 285 * @param string The document title. 286 */ 287 public void setTitle(String string) { 288 title = string; 289 } 290 291 /** 292 * Indicator for labels being translated. We do not do the translations here. 293 * We expect that the translation is done at the application level. This can 294 * therefore be used to decide if the document is already translated (so you 295 * don't need to do it over again. 296 * see net.sf.jolene.struts.DomletAction for the struts implementation 297 * 298 * @param translated set to true if the doc has been translated. 299 */ 300 public void setTranslated(boolean translated) { 301 this.translated = translated; 302 } 303 304 /** 305 * Writes the document to the specified fileName. 306 * 307 * @param filename The filename to write to. 308 * @throws java.io.IOException if there's an IOException 309 */ 310 public void stream(String filename) throws IOException { 311 BufferedWriter out = null; 312 try { 313 out = new BufferedWriter(new FileWriter(filename)); 314 stream(out); 315 } finally { 316 if (out != null) { 317 try { 318 out.close(); 319 } catch (IOException e) { 320 } 321 } 322 } 323 } 324 325 /** 326 * Writes the document to the specified Writer object and closes the Writer. 327 * 328 * @param out an output Writer to write to. 329 * @throws java.io.IOException it there's an IOException 330 */ 331 public void stream(Writer out) throws IOException { 332 try { 333 String ls = System.getProperty("line.separator"); 334 335 if (doctype != null && doctype.trim().length() > 0) { 336 out.write(doctype + ls); 337 } 338 out.write("<html>" + ls); 339 out.write("<head>" + ls); 340 for (Header aHead : head) { 341 out.write(aHead.getBeforeText() + aHead + aHead.getAfterText() + ls); 342 } 343 out.write("<title>" + getTitle() + "</title>" + ls); 344 out.write("</head>" + ls); 345 346 out.write("<body "); 347 for (String s : body.keySet()) { 348 out.write(s + "=\"" + body.getAttribute(s) + "\" "); 349 } 350 out.write(">" + ls); 351 352 // For each form, put the text from the start of the body text to 353 // the startpoint of the form. 354 int offset; 355 offset = 0; 356 Form form; 357 358 for (int j = 0; j < forms.size(); j++) { 359 form = (Form) forms(j); 360 // Write whatever text exist between previous form (or start of doc) and current form. 361 out.write(bodyText.substring(offset, form.getStartPoint())); 362 out.write(form.getBeforeText() + form + form.getAfterText()); 363 offset = form.getEndPoint() + 1; 364 365 for (int k = 0; k < form.elements.size(); k++) { 366 //System.out.print("k = " + k + " " + form.elements(k).startPoint + " " + form.elements(k).endPoint + " " + offset); 367 //System.out.println(" ID: " + form.elements(k).getAttribute("id")); 368 369 out.write(bodyText.substring(offset, form.elements(k).getStartPoint())); 370 out.write(form.elements(k).getBeforeText() + form.elements(k) + form.elements(k).getAfterText()); 371 offset = form.elements(k).getEndPoint() + 1; 372 } 373 374 // if this is the last form write the remaining text. 375 if (j == forms.size() - 1) { 376 out.write(bodyText.substring(offset, bodyText.length())); 377 } 378 } 379 380 out.write(ls); 381 out.write("</body>" + ls); 382 out.write("</html>"); 383 out.flush(); 384 } finally { 385 try { 386 out.close(); 387 } catch (IOException e) { 388 } 389 } 390 } 391 392 /** 393 * Returns the document file name. 394 * 395 * @return String 396 */ 397 public String toString() { 398 return fileName; 399 } 400 401 //================================================ 402 403 void open() throws IOException, MalformedHTMLException { 404 long starttime = System.currentTimeMillis(); 405 long fileSize = new File(fileName).length(); 406 407 log.debug("FileName:" + fileName + " fileSize: " + fileSize); 408 BufferedReader reader = new BufferedReader(new FileReader(fileName)); 409 char[] buff = new char[(int) fileSize]; 410 411 String fileBuffer = ""; 412 try { 413 int n = reader.read(buff, 0, (int) fileSize); 414 415 // Convert char array to StringBuffer 416 fileBuffer = new String(buff, 0, n); 417 } 418 419 finally { 420 try { 421 reader.close(); 422 } catch (IOException e) { 423 } 424 } 425 426 String workBuffer = new String(fileBuffer); 427 428 new Parser(workBuffer, this); 429 430 if ("true".equalsIgnoreCase(Prefs.GenerateLocaleStrings.getValue())) { 431 generateLocaleStrings(); 432 } 433 434 log.info("Time to open doc: " + (System.currentTimeMillis() - starttime)); 435 } 436 437 private void generateLocaleStrings() throws IOException { 438 Properties properties = new Properties(); 439 //@todo - so many issues here. Null pointer possibility, What value for objects? Which objects? Should they include html? NO! Wrong place for the code. Where to create the file? 440 441 FileOutputStream out = null; 442 FileInputStream in = null; 443 try { 444 File f = new File("resources/application.properties"); 445 f.createNewFile(); 446 in = new FileInputStream(f); 447 properties.load(in); 448 out = new FileOutputStream(f); 449 450 Form ff; 451 StringBuilder objectName; 452 453 log.info("FILENAME: " + this.fileName); 454 String docName; 455 docName = this.fileName.substring(this.fileName.lastIndexOf("/") + 1); 456 docName = docName.substring(0, docName.indexOf(".")); 457 log.info("DOC NAME: " + docName); 458 for (int j = 0; j < forms.size(); j++) { 459 objectName = new StringBuilder(); 460 objectName.append(docName).append('.'); 461 462 ff = (Form) forms(j); 463 if (ff.getName().trim().length() > 0) { 464 objectName.append(ff.getName()).append('.'); 465 } else { 466 objectName.append("form").append(j + 1).append('.'); 467 } 468 469 String elementName; 470 HTMLElement element; 471 for (int k = 0; k < ff.elements.size(); k++) { 472 element = ff.elements(k); 473 if (element.hasAttribute("name")) { 474 elementName = element.getAttribute("name"); 475 } else if (element.hasAttribute("id")) { 476 elementName = element.getAttribute("id"); 477 } else { 478 elementName = element.getTag().toString() + k; 479 } 480 if (element.getValue() != null) { 481 properties.put(objectName + elementName, element.getValue()); 482 } 483 } 484 } 485 properties.store(out, "---Locale strings written by jolene---"); 486 out.close(); 487 in.close(); 488 } finally { 489 if (out != null) { 490 try { 491 out.close(); 492 } catch (IOException e) { 493 log.warn(e.getMessage(), e); 494 } 495 try { 496 in.close(); 497 } catch (IOException e) { 498 log.warn(e.getMessage(), e); 499 } 500 } 501 } 502 } 503 504 //-------------------------------------------------------- 505 506 //@TODO Add call for JUnit tests 507 508 void streamObjects() { 509 Form ff; 510 511 for (int j = 0; j < forms.size(); j++) { 512 ff = (Form) forms(j); 513 514 System.out.println("FORM: " + j + ff); 515 for (int k = 0; k < ff.elements.size(); k++) { 516 System.out.print("" + ff.elements(k)); 517 if (ff.elements(k).hasAttribute("name")) { 518 System.out.println("NAME: " + ff.elements(k).getAttribute("name")); 519 } else { 520 if (ff.elements(k).hasAttribute("id")) { 521 System.out.println("ID: " + ff.elements(k).getAttribute("ID")); 522 } else { 523 System.out.println(); 524 } 525 } 526 } 527 } 528 } 529 530 /** 531 * For JUnit 532 */ 533 void streamResults(Writer writer) throws IOException { 534 for (int j = 0; j < forms.size(); j++) { 535 writer.write("form : " + j + " " + forms(j) + "\n"); 536 for (int k = 0; k < forms(j).elements.size(); k++) { 537 writer.write("element : " + k + "\n" + forms(j).elements(k) + "\n"); 538 } 539 } 540 } 541 542 private void translatePaths() { 543 HTMLElement element; 544 String src; 545 546 for (int j = 0; j < this.forms.size(); j++) { 547 for (int k = 0; k < this.forms.get(j).getElementCount(); k++) { 548 element = this.forms.get(j).elements(k); 549 if (element.getTag().equals(Tags.img) && element.hasAttribute("src")) { 550 src = translatePath(element.getAttribute("src")); 551 element.setAttribute("src", src); 552 } 553 } 554 } 555 556 for (int j = 0; j < this.head.size(); j++) { 557 element = this.head.get(j); 558 559 if (element.tag == Tags.script && element.hasAttribute("src")) { 560 src = translatePath(element.getAttribute("src")); 561 element.setAttribute("src", src); 562 } else if (element.tag == Tags.link) { 563 if (element.hasAttribute("href")) { 564 src = translatePath(element.getAttribute("href")); 565 element.setAttribute("href", src); 566 } 567 568 if (element.hasAttribute("src")) { 569 src = translatePath(element.getAttribute("srg")); 570 element.setAttribute("src", src); 571 } 572 } 573 } 574 } 575 576 private String translatePath(String path) { 577 if (path == null) { 578 // If an element does not have a src or href attribute then it will be assigned blank 579 return ""; 580 } 581 if (path.toLowerCase().startsWith("http")) { 582 // leave it alone, the path exists on some other server. 583 return path; 584 } 585 path = path.replace('\\', '/'); 586 StringTokenizer st = new StringTokenizer(path, "/"); 587 StringBuffer endPath = new StringBuffer(""); 588 String pathElement; 589 while (st.hasMoreElements()) { 590 pathElement = st.nextElement().toString(); 591 if (pathElement.equals(".") || pathElement.equals("..")) { 592 } else { 593 endPath.append('/').append(pathElement); 594 } 595 } 596 597 path = contextPath + endPath; 598 599 return path; 600 } 601 }