/* * Weblounge: Web Content Management System * Copyright (c) 2003 - 2011 The Weblounge Team * http://entwinemedia.com/weblounge * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package ch.entwine.weblounge.common.impl.content; import ch.entwine.weblounge.common.impl.security.UserImpl; import ch.entwine.weblounge.common.impl.util.WebloungeDateFormat; import ch.entwine.weblounge.common.impl.util.xml.XPathHelper; import ch.entwine.weblounge.common.security.User; import org.w3c.dom.Node; import java.text.ParseException; import java.util.Date; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; /** * The modification context contains information about when an object was * modified and who the modifier was. It can be used by <code>Modifiable</code> * objects as the backing implementation. * <p> * The context adds additional means of specifying and querying modifier and * modification date. It also allows for easy serialization and deserialization * of <code>Modifiable</code> data. * <p> * Following is an example of the data structure that the modification context * is able to handle: * * <pre> * <modified> * <user id="john" realm="testland">John Doe</user> * <date>2009/11/06 08:52:52 GMT</date> * </modified> * </pre> * * @see ch.entwine.weblounge.common.content.Modifiable */ public class ModificationContext implements Cloneable { /** Modification date */ protected Date modificationDate = null; /** Editor */ protected User modifier = null; /** * Creates a modification context with today's date and no modifier. */ public ModificationContext() { this(null, null); } /** * Creates a modification context with the given modifier and the modification * time reflecting the current date. * * @param modifier * the modifying user */ public ModificationContext(User modifier) { this(new Date(), modifier); } /** * Creates a modification context reflecting a modification by the specified * user and at the given date. * <p> * Note that this method will cut off the millisecond portion of the date. * </p> * * @param date * the modification date * @param modifier * the modifying user */ public ModificationContext(Date date, User modifier) { this.modifier = modifier; this.modificationDate = cutOffMillis(date); } /** * Returns the modification date. * * @return the modification date */ public Date getModificationDate() { return modificationDate; } /** * Returns the modifier. * * @return the modifier */ public User getModifier() { return modifier; } /** * Returns <code>true</code> if the object was modified with respect to the * current date (as in <code>new Date()<code>). * * @return <code>true</code> if the object was modified */ public boolean isModified() { return isModifiedBefore(new Date()); } /** * Returns <code>true</code> if the object was modified prior to the given * date. * * @return <code>true</code> if the object was modified prior to * <code>date</code> */ public boolean isModifiedAfter(Date date) { return modificationDate != null && modificationDate.after(cutOffMillis(date)); } /** * Returns <code>true</code> if the object was modified after the given date. * * @return <code>true</code> if the object was modified after to * <code>date</code> */ public boolean isModifiedBefore(Date date) { return modificationDate != null && modificationDate.before(cutOffMillis(date)); } /** * Sets the modifier along with the modification date. * <p> * Note that this method will cut off the millisecond portion of the date. * </p> * * @param user * the modifier * @param date * the modification date */ public void setModified(User user, Date date) { this.modifier = user; this.modificationDate = cutOffMillis(date); } /** * Cut off the milliseconds from the date. * * @param date * with milliseconds * @return date without milliseconds */ private Date cutOffMillis(Date date) { if (date == null) return null; long time = date.getTime() / 1000L; return new Date(time * 1000L); } /** * Sets the modification date. * <p> * Note that this method will cut off the millisecond portion of the date. * </p> * * @param date * the modification date */ public void setModificationDate(Date date) { this.modificationDate = cutOffMillis(date); } /** * Sets the user that modified the object. * * @param modifier * the modifier */ public void setModifier(User modifier) { this.modifier = modifier; } /** * Returns a copy of this modification context. * * @see java.lang.Object#clone() */ public Object clone() throws CloneNotSupportedException { ModificationContext ctxt = (ModificationContext) super.clone(); if (modifier != null) ctxt.modifier = (User) modifier.clone(); if (modificationDate != null) ctxt.modificationDate = (Date) modificationDate.clone(); return ctxt; } /** * Initializes this context from an XML node that was generated using * {@link #toXml()}. * <p> * To speed things up, you might consider using the second signature that uses * an existing <code>XPath</code> instance instead of creating a new one. * * @param context * the publish context node * @throws IllegalStateException * if the context cannot be parsed * @see #fromXml(Node, XPath) * @see #toXml() */ public static ModificationContext fromXml(Node context) throws IllegalStateException { XPath xpath = XPathFactory.newInstance().newXPath(); return fromXml(context, xpath); } /** * Initializes this context from an XML node that was generated using * {@link #toXml()}. * * @param context * the publish context node * @param xpath * the xpath processor * @throws IllegalStateException * if the context cannot be parsed * @see #toXml() */ public static ModificationContext fromXml(Node context, XPath xpath) throws IllegalStateException { Node contextRoot = XPathHelper.select(context, "/modified", xpath); if (contextRoot == null) return null; // Modifying user ModificationContext ctx = new ModificationContext(); Node creator = XPathHelper.select(contextRoot, "/modified/user", xpath); if (creator == null) throw new IllegalStateException("Modifier cannot be null"); ctx.setModifier(UserImpl.fromXml(creator, xpath)); // Modification date String date = XPathHelper.valueOf(contextRoot, "/modified/date", xpath); if (date == null) throw new IllegalStateException("Date cannot be null"); try { ctx.setModificationDate(WebloungeDateFormat.parseStatic(date)); } catch (ParseException e) { throw new IllegalStateException("The modification date '" + date + "' cannot be parsed", e); } return ctx; } /** * Returns an <code>XML</code> representation of the context, that will look * similar to the following example: * * <pre> * <modified> * <user id="john" realm="testland">John Doe</user> * <date>2009/11/06 08:52:52 GMT</date> * </modified> * </pre> * * Use {@link #fromXml(Node))} or {@link #fromXml(Node, XPath)} to create a * <code>ModificationContext</code> from the serialized output of this method. * * @return the <code>XML</code> representation of the context * @see #fromXml(Node) * @see #fromXml(Node, XPath) */ public String toXml() { if (modifier == null || modificationDate == null) return ""; StringBuffer b = new StringBuffer(); b.append("<modified>"); // User if (modifier != null) { b.append("<user"); // id b.append(" id=\""); b.append(modifier.getLogin()); b.append("\""); // realm if (modifier.getRealm() != null) { b.append(" realm=\""); b.append(modifier.getRealm()); b.append("\""); } b.append(">"); // name if (modifier.getName() != null) { b.append("<![CDATA[").append(modifier.getName()).append("]]>"); } b.append("</user>"); } if (modificationDate != null) { b.append("<date>"); b.append(WebloungeDateFormat.formatStatic(modificationDate)); b.append("</date>"); } b.append("</modified>"); return b.toString(); } }