/** * Copyright 1999-2009 The Pegadi Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.pegadi.model; // XML imports import no.dusken.common.model.Person; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.Serializable; import java.io.StringReader; import java.io.StringWriter; import java.util.Date; /** * This class represents an article. * * @author Håvard Wigtil <havardw at pvv.org> * @author Jørgen Binningsbø <jb@underdusken.no> * @version $Revision: 2774 $, $Date$ */ public class Article implements Serializable, Comparable { /** * The minimum amount of text an xml document can contain */ private static final int XML_MINIMUM = 10; /** * The maximum length of the article name */ private static final int MAX_NAME_LENGTH = 128; /** * The XML representation of this document. */ private Document mDoc; /** * the name of this article */ private String articleName; /** * the ID of this article */ private int id; /** * the journalist owning this article */ private Person journalist; /** * the photographer assigned to this article */ private Person photographer; /** * the description of this article */ private String description; /** * is the article-xml editable? */ private boolean canEditXML = false; /** * is this article viewable */ private boolean canView = true; /** * the publication where this article is scheduled for printing */ private Publication publication; /** * the section this article belongs to */ private Section section; /** * the type of this article */ private ArticleType articleType; /** * the status of this article */ private ArticleStatus articleStatus; /** * the current number of characters for this article. warning: * only updated when the article is saved. */ private int currentNumberOfCharacters; /** * the wanted number of characters for this article. */ private int wantedNumberOfCharacters; /** * the wanted number of pages for this article. */ private float wantedNumberOfPages; /** * The serialized XML-text of this Article */ private String text; /** * The date the article was last saved */ private Date lastSaved; /** * Compares the article to another article by name. * If name is equal it compares by ID * * @param o the object to compare to * @return positive if name is after, negative if before */ public int compareTo(Object o) { Article art = (Article) o; int lastCmp = this.getName().compareTo(art.getName()); if (lastCmp == 0) { lastCmp = this.getId() - art.getId(); } return lastCmp; } /** * Returns the ID of this article. * * @return an <code>int</code> value, or <code>null</code> if no ID is set. */ public int getId() { return id; } /** * Sets the ID of this article * * @param id the id of the article */ public void setId(int id) { this.id = id; } /** * Returns the name of this article * * @return the name of the article */ public String getName() { return articleName; } /** * Returns the string representation of this article * * @return the string representation of the article */ public String toString() { return getName(); } /** * Sets the name of this article * * @param articleName the name of the article */ public void setName(String articleName) { if(articleName == null || articleName.length() < MAX_NAME_LENGTH) this.articleName = articleName; else { this.articleName = articleName.substring(0, MAX_NAME_LENGTH); } } /** * Returns the journalist assigned to this article * * @return the journalist user object */ public Person getJournalist() { return journalist; } /** * Sets the journalist assigned to this article * * @param journalist the journalist user object */ public void setJournalist(Person journalist) { this.journalist = journalist; } /** * Returns the photographer assigned to this article * * @return the photographer user object */ public Person getPhotographer() { return photographer; } /** * Sets the photographer assigned to this article * * @param photographer the photographer user object */ public void setPhotographer(Person photographer) { this.photographer = photographer; } /** * Gets the description of this article * * @return the description */ public String getDescription() { return description; } /** * Sets the description of this article * * @param description the description */ public void setDescription(String description) { this.description = description; } /** * Gets the publication this article is scheduled for * * @return the publication object */ public Publication getPublication() { return publication; } /** * Sets the publication for this article * * @param publication the publication object */ public void setPublication(Publication publication) { this.publication = publication; } /** * Sets the text for this article * * @param doc the parsed XML DOM Document object */ public void setDoc(Document doc) { this.mDoc = doc; } /** * Sets the XML text of this article. * * @param text the serialized XML text */ public void setText(String text) { this.text = text; } /** * Gets the current article-type * * @return the ArticleType */ public ArticleType getArticleType() { return articleType; } /** * Sets the current article-type * * @param articleType the ArticleType */ public void setArticleType(ArticleType articleType) { this.articleType = articleType; } /** * Returns the current status of this article * * @return the ArticleStatus */ public ArticleStatus getArticleStatus() { return articleStatus; } /** * Sets the current status of this article * * @param articleStatus the ArticeStatus */ public void setArticleStatus(ArticleStatus articleStatus) { this.articleStatus = articleStatus; } /** * Returns the number of characters in this article * * @return the current number of characters */ public int getCurrentNumberOfCharacters() { return currentNumberOfCharacters; } /** * Sets the current number of characters in this articel * * @param currentNumberOfCharacters the current number of characters */ public void setCurrentNumberOfCharacters(int currentNumberOfCharacters) { this.currentNumberOfCharacters = currentNumberOfCharacters; } /** * Returns the wanted number of characters in this article * * @return the number of wanted (planned) characters */ public int getWantedNumberOfCharacters() { return wantedNumberOfCharacters; } /** * Sets the wanted number of characters in this article * * @param wantedNumberOfCharacters the number of wanted characters */ public void setWantedNumberOfCharacters(int wantedNumberOfCharacters) { this.wantedNumberOfCharacters = wantedNumberOfCharacters; } /** * Get the value of wantedNumberOfPages. * * @return value of wantedNumberOfPages. */ public float getWantedNumberOfPages() { return wantedNumberOfPages; } /** * Set the value of wantedNumberOfPages. * * @param v Value to assign to wantedNumberOfPages. */ public void setWantedNumberOfPages(float v) { this.wantedNumberOfPages = v; } /** * Get the value of section. * * @return value of section. */ public Section getSection() { return section; } /** * Set the value of section. * * @param v Value to assign to section. */ public void setSection(Section v) { this.section = v; } /** * Set the value of canEditXML. * * @param v Value to assign to canEditXML. */ public void setCanEditXML(boolean v) { this.canEditXML = v; } /** * Sets the date this article was last saved * * @param lastSaved date of last save */ public void setLastSaved(Date lastSaved) { this.lastSaved = lastSaved; } /** * Returns the Date the article was last saved * * @return date of last save */ public Date getLastSaved() { return lastSaved; } /** * Creates a new article with a given ID (ie: from the database * * @param id The id of this article */ public Article(int id) { this.id = id; } /** * Creates a new, empty <code>Article</code> instance. * All parameter has a <code>null</code> value. */ public Article() { } /** * Returns the DOM-tree representing the text for this article. * * @return a <code>Document</code> value */ public Document getDocument() { return mDoc; } /** * Returns the document type for this article. * * @return a <code>String</code> value */ public String getDocumentType() { try { return mDoc.getDoctype().getName(); } catch (Exception e) { Logger log = LoggerFactory.getLogger(getClass()); log.error("Exception getting document type!", e); return null; } } /** * Returns true if this article has any text at all. * * @return a <code>boolean</code> value */ public boolean hasText() { return text == null || text.length() > XML_MINIMUM; } /** * Parses the text in the article and creates the DOM-tree in the * variable mDoc. * * @return <code>true</code> if parsing succeeded. */ public boolean parseText() { boolean outvar = true; if (!this.hasText()) { Logger log = LoggerFactory.getLogger(getClass()); log.error("Article got no text!"); outvar = false; } else { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = null; try { factory.setIgnoringElementContentWhitespace(false); builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException pce) { Logger log = LoggerFactory.getLogger(getClass()); log.error("Parser configuration exception", pce); outvar = false; } StringReader stringReader = new StringReader(text); try { mDoc = builder.parse(new InputSource(stringReader)); } catch (SAXException se) { Logger log = LoggerFactory.getLogger(getClass()); log.error("Error parsing", se); outvar = false; } catch (java.io.IOException ioe) { Logger log = LoggerFactory.getLogger(getClass()); log.error("Error parsing", ioe); outvar = false; } } return outvar; } /** * Returns the text. (ie. the serialized XML for this article) * * @return a <code>String</code> value */ public String getText() { return text; } /** * Serializes the DOM document and saves it to text. * * @return <code>true</code> if serialization succeded */ public boolean serialize() { try { StringWriter newText = new StringWriter(); Transformer t = TransformerFactory.newInstance().newTransformer(); t.setOutputProperty(OutputKeys.ENCODING, "utf-8"); t.transform(new DOMSource(mDoc), new StreamResult(newText)); this.setText(newText.toString()); return true; } catch (TransformerException e) { Logger log = LoggerFactory.getLogger(getClass()); log.error("TransformerException serializing article"); return false; } } /** * Clone this article, except from the <code>mDoc</code> field. * <code>mDoc</code> is always set to null, make sure that the * xml is serialized by a call to {@link #serialize serialize()} * before cloning. * * @return The clone. */ public Object clone() { Article clone = new Article(this.id); clone.articleName = articleName == null ? null : articleName; clone.articleStatus = articleStatus == null ? null : (ArticleStatus) articleStatus.clone(); clone.articleType = articleType == null ? null : (ArticleType) articleType.clone(); clone.canEditXML = this.canEditXML; clone.canView = this.canView; clone.section = section == null ? null : (Section) section.clone(); clone.description = description == null ? null : description; clone.journalist = journalist; // FIXME: Should clone XML clone.mDoc = null; clone.photographer = photographer; clone.publication = publication == null ? null : (Publication) publication.clone(); clone.text = text == null ? null : text; clone.currentNumberOfCharacters = this.currentNumberOfCharacters; clone.wantedNumberOfCharacters = this.wantedNumberOfCharacters; clone.wantedNumberOfPages = this.wantedNumberOfPages; clone.lastSaved = this.lastSaved; return clone; } /** * Suggests a file name in a quite crude way * * @param maxChars the maximum number of characters to suggest N * @return the suggested file name */ public String suggestFileName(int maxChars) { String suggestion = articleName; StringBuilder clean = new StringBuilder(); for (int i = 0; i < suggestion.length(); i++) { if (suggestion.charAt(i) == 'æ') { clean.append("ae"); } else if (suggestion.charAt(i) == 'ø') { clean.append("o"); } else if (suggestion.charAt(i) == 'å') { clean.append("aa"); } else if (suggestion.charAt(i) == 'Æ') { clean.append("Ae"); } else if (suggestion.charAt(i) == 'Ø') { clean.append("O"); } else if (suggestion.charAt(i) == 'Å') { clean.append("Aa"); } else if (Character.isLetter(suggestion.charAt(i)) || Character.isDigit(suggestion.charAt(i)) || Character.isWhitespace(suggestion.charAt(i))) { clean.append(suggestion.charAt(i)); } } suggestion = clean.toString(); if (suggestion.length() > maxChars) { suggestion = suggestion.substring(0, maxChars); } suggestion = suggestion.trim(); suggestion = suggestion.replace(' ', '_'); return suggestion; } }