/*
* $Id$
*
* Copyright 2006, The jCoderZ.org Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
* * Neither the name of the jCoderZ.org Project nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jcoderz.commons.taskdefs;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
class AppInfoSaxHandler
extends DefaultHandler
{
/** Application Identifier of fawkeZ. */
public static final int APPLICATION_ID_FWK = 1;
/** Application Identifier of asf. */
public static final int APPLICATION_ID_ASF = 100;
/** Application Identifier of ppg. */
public static final int APPLICATION_ID_PPG = 101;
/** Application Identifier of taco. */
public static final int APPLICATION_ID_TAC = 102;
/** Application Identifier of application (amandel). */
public static final int APPLICATION_ID_ACM = 120;
/** Application Identifier of application (amandel). */
public static final int APPLICATION_ID_ACR = 121;
/** Application Identifier of application (amandel). */
public static final int APPLICATION_ID_DMB = 122;
private static final Pattern REGEX_SINGLE_QUOTES
= Pattern.compile(".*[^']'[^'].*",
Pattern.DOTALL + Pattern.MULTILINE);
private static final Pattern REGEX_VARIABLES
= Pattern.compile(".*\\{.+\\}.*",
Pattern.DOTALL + Pattern.MULTILINE);
private static final String EMPTY_STRING = "";
private SAXParseException mSaxParseException = null;
private boolean mValidationError = false;
/** Maps application id (Integer) to app short-name (String). */
private final NamedMap mMap = new NamedMap("applications");
private String mCurrentAppName = EMPTY_STRING;
private String mCurrentGrpName = EMPTY_STRING;
private String mCurrentMsgName = EMPTY_STRING;
private int mCurrentAppId = 0;
private int mCurrentGrpId = 0;
private int mCurrentMsgId = 0;
private final List mWarningMessages = new ArrayList();
private final StringBuffer mBuffer = new StringBuffer();
private boolean mCaptureCharacters = false;
/**
* Constructor.
*/
public AppInfoSaxHandler ()
{
mMap.registerApplication(APPLICATION_ID_FWK, "FWK");
mMap.registerApplication(APPLICATION_ID_ASF, "ASF");
mMap.registerApplication(APPLICATION_ID_PPG, "PPG");
mMap.registerApplication(APPLICATION_ID_TAC, "TAC");
mMap.registerApplication(APPLICATION_ID_ACR, "ACR");
mMap.registerApplication(APPLICATION_ID_ACM, "ACM");
mMap.registerApplication(APPLICATION_ID_DMB, "DMB");
}
/** {@inheritDoc} */
public void error (SAXParseException exception)
{
mValidationError = true;
mSaxParseException = exception;
}
/** {@inheritDoc} */
public void fatalError (SAXParseException exception)
{
mValidationError = true;
mSaxParseException = exception;
}
/** {@inheritDoc} */
public void warning (SAXParseException exception)
{
// NOP
}
/** {@inheritDoc} */
public void startDocument ()
{
reset();
}
/** {@inheritDoc} */
public void startElement (String uri, String localName,
String qName, Attributes attributes)
throws SAXException
{
try
{
if ("application".equals(localName))
{
mCurrentAppName = attributes.getValue("short-name");
mCurrentAppId = Integer.parseInt(attributes.getValue("id"));
mMap.addApplication(mCurrentAppId, mCurrentAppName);
}
else if ("group".equals(localName))
{
mCurrentGrpName = attributes.getValue("short-name");
mCurrentGrpId = Integer.parseInt(attributes.getValue("id"));
mMap.getApp(mCurrentAppId).addGroup(mCurrentGrpId, mCurrentGrpName);
}
else if ("message".equals(localName))
{
mCurrentMsgName = attributes.getValue("name");
mCurrentMsgId = Integer.parseInt(attributes.getValue("id"));
mMap.getApp(mCurrentAppId).getGrp(
mCurrentGrpId).addMessage(mCurrentMsgId, mCurrentMsgName);
}
else if ("text".equals(localName) || "solution".equals(localName))
{
captureCharacters();
}
}
catch (AppInfoException e)
{
throw new SAXException(e);
}
}
/** {@inheritDoc} */
public void endElement (String uri, String localName, String qName)
{
if ("text".equals(localName))
{
final String cdata = characters().trim();
validateText(cdata);
}
else if ("solution".equals(localName))
{
final String cdata = characters().trim();
validateSolution(cdata);
}
else if ("description".equals(localName))
{
final String cdata = characters().trim();
validateDescription(cdata);
}
else if ("procedure".equals(localName))
{
final String cdata = characters().trim();
validateProcedure(cdata);
}
else if ("validation".equals(localName))
{
final String cdata = characters().trim();
validateValidation(cdata);
}
else if ("application".equals(localName))
{
mCurrentAppId = 0;
mCurrentAppName = EMPTY_STRING;
}
else if ("group".equals(localName))
{
mCurrentGrpId = 0;
mCurrentGrpName = EMPTY_STRING;
}
else if ("message".equals(localName))
{
mCurrentMsgId = 0;
mCurrentMsgName = EMPTY_STRING;
}
}
/** {@inheritDoc} */
public void characters (char[] ch, int start, int length)
{
if (mCaptureCharacters)
{
mBuffer.append(ch, start, length);
}
}
/**
* Returns the captured characters and <b>clears</b> the internal
* buffer.
* @return the captured characters.
*/
public String characters ()
{
final String result = mBuffer.toString();
mBuffer.setLength(0);
mCaptureCharacters = false;
return result;
}
/**
* Returns <tt>true</tt> if there are warning messages available.
* @return <tt>true</tt> if there are warning messages available;
* <tt>false</tt> otherwise.
*/
public boolean hasWarningMessages ()
{
return !mWarningMessages.isEmpty();
}
/**
* Returns a list<String> of warning messages.
* @return a list<String> of warning messages.
*/
public List getWarningMessages ()
{
return mWarningMessages;
}
void captureCharacters ()
{
mCaptureCharacters = true;
}
SAXParseException getParseException ()
{
return mSaxParseException;
}
boolean hasValidationErrors ()
{
return mValidationError;
}
private void validateText (String cdata)
{
if (cdata != null)
{
if (REGEX_SINGLE_QUOTES.matcher(cdata).matches())
{
warn("The text element contains single quotes.");
}
}
}
private void validateSolution (String cdata)
{
if (cdata != null)
{
if (REGEX_VARIABLES.matcher(cdata).matches())
{
warn("The solution element MUST NOT use variables: " + cdata);
}
}
}
private void validateDescription (String cdata)
{
if (cdata != null)
{
if (REGEX_VARIABLES.matcher(cdata).matches())
{
warn("The solution element MUST NOT use variables: " + cdata);
}
}
}
private void validateProcedure (String cdata)
{
if (cdata != null)
{
if (REGEX_VARIABLES.matcher(cdata).matches())
{
warn("The solution element MUST NOT use variables: " + cdata);
}
}
}
private void validateValidation (String cdata)
{
if (cdata != null)
{
if (REGEX_VARIABLES.matcher(cdata).matches())
{
warn("The solution element MUST NOT use variables: " + cdata);
}
}
}
private void warn (String message)
{
mWarningMessages.add("[" + mCurrentAppName + "."
+ mCurrentGrpName + "." + mCurrentMsgName + "] " + message);
}
private void reset ()
{
mBuffer.setLength(0);
mCaptureCharacters = false;
mCurrentAppName = EMPTY_STRING;
mCurrentGrpName = EMPTY_STRING;
mCurrentMsgName = EMPTY_STRING;
mWarningMessages.clear();
mCurrentAppId = 0;
mCurrentGrpId = 0;
mCurrentMsgId = 0;
}
private static class NamedMap
{
private final Map mMap = new HashMap();
private final String mName;
public NamedMap (String name)
{
mName = name;
}
public String getName ()
{
return mName;
}
public boolean contains (int id)
{
return mMap.containsKey(new Integer(id));
}
public NamedMap getApp (int id)
{
return (NamedMap) mMap.get(new Integer(id));
}
public void registerApplication (int id, String appName)
{
mMap.put(new Integer(id), new NamedMap(appName));
}
public void addApplication (int id, String appName)
throws AppInfoException
{
if (!contains(id))
{
throw new AppInfoException(
"Application " + appName + " with the identifier "
+ id + " is not registered. "
+ "Registered applications are " + mMap);
}
}
public NamedMap getGrp (int id)
{
return (NamedMap) mMap.get(new Integer(id));
}
public void addGroup (int id, String groupName)
throws AppInfoException
{
if (contains(id))
{
final String registeredGrpName
= ((NamedMap) mMap.get(new Integer(id))).getName();
throw new AppInfoException("The group " + groupName
+ " with the id " + id
+ " is already assigned to " + registeredGrpName + ".");
}
mMap.put(new Integer(id), new NamedMap(groupName));
}
public void addMessage (int id, String messageName)
throws AppInfoException
{
if (contains(id))
{
final String registeredMsgName = (String) mMap.get(new Integer(id));
throw new AppInfoException("The message " + messageName
+ " with the id " + id
+ " is already assigned to " + registeredMsgName + ".");
}
mMap.put(new Integer(id), messageName);
}
public String toString ()
{
final StringBuffer sb = new StringBuffer();
sb.append(mName);
sb.append(' ');
sb.append(mMap);
return sb.toString();
}
}
private static class AppInfoException
extends Exception
{
private static final long serialVersionUID = 1L;
public AppInfoException (String msg)
{
super(msg);
}
public AppInfoException (String msg, Throwable cause)
{
super(msg, cause);
}
}
}