/**
* @Created Nov 7, 2011 1:03:14 PM
* @author cry30
*/
package com.philip.journal.home.service;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import com.philip.journal.common.BeanUtils;
import com.philip.journal.common.XmlUtils;
import com.philip.journal.core.Messages;
import com.philip.journal.core.Messages.Error;
import com.philip.journal.core.bean.User;
import com.philip.journal.core.exception.JournalException;
import com.philip.journal.core.service.BaseService;
import com.philip.journal.home.bean.Branch;
import com.philip.journal.home.bean.Entry;
import com.philip.journal.home.dao.BranchDAO;
import com.philip.journal.home.dao.EntryDAO;
/**
*
*/
public class XmlHelper extends BaseService {
/** XML Tag. RTFC. */
public static final String TAG_BRANCH = "Branch";
/** XML Tag. RTFC. */
public static final String TAG_BRANCHES = "Branches";
/** XML Tag. RTFC. */
public static final String TAG_ENTRY = "Entry";
/** XML Tag. RTFC. */
public static final String TAG_ENTRIES = "Entries";
/** Excluded bean properties for xml conversion. */
static final String[] EXCLUDED_PROPS = {
"parent",
"primaryKeyField",
"branch",
"treePath" };
/** Time format. */
private final transient DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
/** Date format. */
private final transient DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
/**
* Recursive function to convert a branch and it's descendants into document.
*
* @param doc - Document instance for instantiating Element.
* @param targetElement - the target Element for the conversion.
* @param branch - starting branch for the conversion.
*
* @exception IllegalArgumentException when any of the 3 parameter is null.
*/
public void convertNodeToXml(final Document doc, final Element targetElement, final Branch branch) {
if (doc == null || targetElement == null || branch == null) {
throw new IllegalArgumentException(Error.IAE_NULL);
}
final Element thisBranchElement = doc.createElement(TAG_BRANCH);
targetElement.appendChild(thisBranchElement);
attachPropertiesToElement(doc, thisBranchElement, branch);
//getLogger().debug("convertNodeToXml: " + branch);
final long currentBranchId = branch.getBranchId();
final List<Branch> children = getDaoFacade().getBranchDAO().readAllByParent(currentBranchId);
if (!children.isEmpty()) {
final Element branchesElement = doc.createElement(TAG_BRANCHES);
thisBranchElement.appendChild(branchesElement);
for (final Branch nextSubBranch : children) {
convertNodeToXml(doc, branchesElement, nextSubBranch);
}
}
final List<Entry> entries = getDaoFacade().getEntryDAO().readAllByBranch(currentBranchId);
if (!entries.isEmpty()) {
final Element entriesElement = doc.createElement(TAG_ENTRIES);
thisBranchElement.appendChild(entriesElement);
for (final Entry nextEntry : entries) {
final Element entryElement = doc.createElement(TAG_ENTRY);
attachPropertiesToElement(doc, entryElement, nextEntry);
entriesElement.appendChild(entryElement);
}
}
}
/**
* Helper method to extract properties of a bean and attach it to a specified target element.
*
* @param doc - Document object for creating the Element instance
* @param targetElement - Element to which the extracted properties will be attached.
* @param bean - The bean whose properties will be converted into Elements.
*
* @exception IllegalArgumentException when any of the 3 parameter is null.
*/
void attachPropertiesToElement(final Document doc, final Element targetElement, final Object bean) {
if (doc == null || targetElement == null || bean == null) {
throw new IllegalArgumentException(Error.IAE_NULL);
}
final String[] beanProperties = BeanUtils.getProperties(bean, EXCLUDED_PROPS);
Element propertyElement;
for (final String property : beanProperties) {
// System.out.println("property: " + property);
propertyElement = doc.createElement(property);
Object value = null;
try {
value = BeanUtils.getProperty(bean, property);
if (value instanceof User) {
value = ((User) value).getUsername();
}
} catch (final Exception criticalError) {
getLogger().debug(criticalError.getMessage(), criticalError);
}
final String stringVal = value == null ? "" : String.valueOf(value);
// System.out.println("Value: " + value);
if (!stringVal.trim().equals("")) {
final Text textValue = XmlUtils.isCData(stringVal) ? doc.createCDATASection(stringVal) : doc
.createTextNode(stringVal);
targetElement.appendChild(propertyElement);
propertyElement.appendChild(textValue);
}
}
}
/**
* Note: RECURSIVE!
*
* Helper method to extract Branch object from Element and send it to DAO for persistence. The branchId will be
* cleared to make this a new insert.
*
* @param branchElement Element representation of the Branch.
* @param parentId parent ID.
* @exception JournalException Application specific exception on DAO calls.
* @throws XPathExpressionException XPath module exception.
* @exception IllegalArgumentException when branchElement is null.
*/
public void extractBranchFromDocument(final Element branchElement, final long parentId)
throws XPathExpressionException
{
if (branchElement == null) {
throw JournalException.wrapperException(new IllegalArgumentException(Error.IAE_NULL));
}
final Branch targetBranch = convertElementToBranch(branchElement, parentId);
final XPath xpath = XPathFactory.newInstance().newXPath();
final Element branchesElement = (Element) xpath.evaluate(TAG_BRANCHES, branchElement, XPathConstants.NODE);
if (branchesElement != null) {
final NodeList subBranchList = (NodeList) xpath.evaluate(TAG_BRANCH, branchesElement,
XPathConstants.NODESET);
for (int i = 0; subBranchList != null && i < subBranchList.getLength(); i++) {
final Element subBranchElement = (Element) subBranchList.item(i);
extractBranchFromDocument(subBranchElement, targetBranch.getBranchId());
}
}
extractEntryFromDocument(branchElement, targetBranch);
}
/**
* TODO: Unit Testing. <br/>
* Refactored out from {@link #extractBranchFromDocument(Element, long)}. Will convert Element to Branch and
* persist.
*
* @param branchElement Branch Element to where we create Entries of.
* @param targetBranch Branch object of the Branch Element.
* @throws XPathExpressionException XPath module exception.
*/
void extractEntryFromDocument(final Element branchElement, final Branch targetBranch)
throws XPathExpressionException
{
final XPath xpath = XPathFactory.newInstance().newXPath();
final EntryDAO entryDao = getDaoFacade().getEntryDAO();
final BranchDAO branchDao = getDaoFacade().getBranchDAO();
final Branch subListParent = branchDao.read(targetBranch.getBranchId());
final Element entriesElement = (Element) xpath.evaluate(TAG_ENTRIES, branchElement, XPathConstants.NODE);
if (entriesElement != null) {
final NodeList entryList = (NodeList) xpath.evaluate(TAG_ENTRY, entriesElement, XPathConstants.NODESET);
final String[] entryProperties = BeanUtils.getProperties(new Entry(), EXCLUDED_PROPS);
final Entry entry = new Entry();
for (int i = 0; entryList != null && i < entryList.getLength(); i++) {
for (final String property : entryProperties) {
final Object val = getElementPropValue((Element) entryList.item(i),
BeanUtils.getPropertyType(entry, property), property);
if (val != null) {
try {
org.apache.commons.beanutils.BeanUtils.setProperty(entry, property, val);
} catch (final Exception e) {
throw new JournalException(e.getMessage(), e);
}
}
}
if (null == entryDao.readByTitle(entry.getTitle(), subListParent.getBranchId())) {
entry.setBranch(subListParent);
entry.setNodeId(0);
entryDao.save(entry);
}
}
}
}
/**
* TODO: Unit test for normal scenario.
*
* Refactored out from {@link #extractBranchFromDocument(Element, long)}. Will convert Element to Branch and
* persist.
*
* @param branchElement Element representation of the Branch.
* @param parentId parent Branch ID.
* @return Branch the converted Branch from Element.
*
* @exception JournalException when:
* <ul>
* <li>Application specific exception on DAO calls.
* <li>null branchElement.
* <li>Invalid parent ID.
* </ul>
* @throws XPathExpressionException XPath module exception.
*/
Branch convertElementToBranch(final Element branchElement, final long parentId) throws XPathExpressionException
{
final BranchDAO branchDao = getDaoFacade().getBranchDAO();
final Branch parent = branchDao.read(parentId);
if (parent == null || branchElement == null) {
throw JournalException.wrapperException(new IllegalArgumentException(Messages.Error.IAE_NULL));
}
Branch targetBranch = new Branch();
final String[] branchProperties = BeanUtils.getProperties(targetBranch, EXCLUDED_PROPS);
for (final String property : branchProperties) {
final Object val = getElementPropValue(branchElement, BeanUtils.getPropertyType(targetBranch, property),
property);
if (val != null) {
try {
org.apache.commons.beanutils.BeanUtils.setProperty(targetBranch, property, val);
} catch (final Exception e) {
throw new JournalException(e.getMessage(), e);
}
}
}
targetBranch.setParent(parent);
final Branch storedBranch = branchDao.readByName(targetBranch.getName(), parentId);
if (storedBranch == null) {
targetBranch.setBranchId(0);
branchDao.save(targetBranch);
} else {
targetBranch = storedBranch;
}
return targetBranch;
}
/**
* I take a xml element and the tag name, look for the tag and get the text content i.e for
*
* <pre>
* <employee>
* <name>John
*
* Note: Closing tags removed due to complaints from Checkstyle.
*
* </pre>
*
* xml snippet if the Element points to employee node and tagName is 'name' I will return John.
*
* @param element xml element object.
* @param valType value type for casting to java object.
* @param property xml property name.
* @return property value of the element property.
* @throws XPathExpressionException thrown by XPatch classes.
*/
Object getElementPropValue(final Element element, final Class<?> valType, final String property)
throws XPathExpressionException {
final XPath xpath = XPathFactory.newInstance().newXPath();
final String xmlString = (String) xpath.evaluate(property, element, XPathConstants.STRING);
Object val = null;
if (xmlString != null) {
final String textVal = xmlString;
if (Long.TYPE.equals(valType)) {
val = Long.parseLong(textVal);
} else if (String.class == valType) {
val = textVal;
} else if (property.endsWith("Time")) {
try {
val = timeFormat.parse(textVal);
} catch (final ParseException ignore) {
getLogger().warn(ignore.getMessage(), ignore);
}
} else if (property.endsWith("Date")) {
try {
val = dateFormat.parse(textVal);
} catch (final ParseException ignore) {
getLogger().warn(ignore.getMessage(), ignore);
}
} else if (User.class == valType) {
val = getDaoFacade().getUserDAO().readByUsername(textVal);
}
}
return val;
}
}