/******************************************************************************* * Copyright (c) 2012 Arapiki Solutions Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * "Peter Smith <psmith@arapiki.com>" - initial API and * implementation and/or initial documentation *******************************************************************************/ package com.buildml.config; import java.util.ArrayList; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.buildml.utils.errors.ErrorCode; /** * A SAX handler class for parsing a ".bmlconfig" file. * * @author "Peter Smith <psmith@arapiki.com>" */ /* package */ class PerTreeConfigSAXHandler extends DefaultHandler { /*=====================================================================================* * TYPES/FIELDS *=====================================================================================*/ /** The {@link PerTreeConfigFile} that this configuration applies to */ private PerTreeConfigFile configFile = null; /** remember whether the starting <bmlconfig> tag has been seen */ private boolean startSeen = false; /** The name of the <alias> we're currently processing */ private String currentAlias = null; /** The list of <package> entries associated with the current <alias> */ private ArrayList<String> currentAliasList; /*=====================================================================================* * CONSTRUCTORS *=====================================================================================*/ /** * Instantiate a new PerTreeConfigSAXHandler object, with a reference to the * {@link PerTreeConfigFile} to be populated. * * @param configFile The {@link PerTreeConfigFile} to be populated. */ public PerTreeConfigSAXHandler(PerTreeConfigFile configFile) { this.configFile = configFile; } /*=====================================================================================* * PUBLIC METHODS *=====================================================================================*/ /** * Handle occurrences of an element start tag. This method is invoked by the SAX Parser * whenever a new element start tag (e.g. <alias>) is identified. See the definition of * import org.xml.sax.helpers.DefaultHandler for parameter details. */ @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { /* The first tag must be <bmlconfig> */ if (!startSeen) { if (localName.equals("bmlconfig")) { startSeen = true; /* validate the schema version number */ String versionString = atts.getValue("version"); if (versionString == null){ throw new SAXException("Invalid file content. Schema version missing."); } try { int versionNum = Integer.valueOf(versionString); if (versionNum != PerTreeConfigFile.SCHEMA_VERSION) { throw new SAXException("Invalid file content. Expected schema " + PerTreeConfigFile.SCHEMA_VERSION + " but file has version " + versionNum + "."); } } catch (NumberFormatException e) { throw new SAXException("Invalid file content. Schema version is not numeric."); } } else { throw new SAXException("Invalid file content. Must start with <bmlconfig>."); } } /* process the <alias> tag by recording the name and initializing data structures */ else if (localName.equals("alias")) { currentAlias = atts.getValue("name"); if (currentAlias == null) { throw new SAXException("Invalid file content. <alias> tag must have a 'name' attribute."); } currentAliasList = new ArrayList<String>(); } /* process the <rootmap> tag by recording the "name" and "path" attributes */ else if (localName.equals("rootmap")) { String rootName = atts.getValue("name"); if (rootName == null) { throw new SAXException("Invalid file content. <rootmap> tag must have a 'name' attribute."); } String nativePath = atts.getValue("path"); if (nativePath == null) { throw new SAXException("Invalid file content. <rootmap> tag must have a 'path' attribute."); } /* add, and report errors */ int rc = configFile.addNativeRootMapping(rootName, nativePath); if (rc == ErrorCode.NOT_FOUND) { throw new SAXException("Invalid file content. <rootmap> has invalid 'name' attribute."); } else if (rc == ErrorCode.BAD_PATH) { throw new SAXException("Invalid file content. <rootmap> has invalid 'path' attribute."); } } /* process the <package> tag by adding the package name to our list. */ else if (localName.equals("package")) { String pkgName = atts.getValue("name"); if (currentAlias == null) { throw new SAXException("Invalid file content. <package> tag must have a 'name' attribute."); } currentAliasList.add(pkgName); } /* else, error */ else { throw new SAXException("Invalid file content. Unrecognized tag: <" + localName + ">"); } } /*-------------------------------------------------------------------------------------*/ /** * Handle occurrences of an element end tag. This method is invoked by the SAX Parser * whenever a new element end tag (e.g. </alias>) is identified. See the definition of * import org.xml.sax.helpers.DefaultHandler for parameter details. */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { /* if we see </alias>, proceed to add the current alias definition */ if (localName.equals("alias")) { if (currentAlias == null) { throw new SAXException("Invalid file content. Unexpected </alias>"); } String packages[] = currentAliasList.toArray(new String[currentAliasList.size()]); int rc = configFile.addAlias(currentAlias, packages); /* * Check for invalid name, but that's all. Don't worry about package names that * aren't defined, since we don't want stale aliases to cause the whole loading * to fail. */ if (rc == ErrorCode.INVALID_NAME) { throw new SAXException("Invalid file content. Invalid alias name: " + currentAlias); } /* reset, in preparation for next time we seen <alias> */ currentAlias = null; currentAliasList = null; } /* else, if we see </package> or </rootmap>, do nothing */ else if (localName.equals("package") || localName.equals("rootmap")) { /* nothing to do - all done when <alias> is seen */ } /* else, seeing </bmlconfig> means we're at the end of the file */ else if (localName.equals("bmlconfig")) { /* nothing to do */ } /* other things are an error */ else { throw new SAXException("Invalid file content. Unrecognized tag: </" + localName + ">"); } } /*-------------------------------------------------------------------------------------*/ }