/**
* Copyright (C) 2012 cogroo <cogroo@cogroo.org>
*
* 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.cogroo.tools.checker.rules.applier;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.cogroo.tools.checker.rules.exception.RulesException;
import org.cogroo.tools.checker.rules.model.ObjectFactory;
import org.cogroo.tools.checker.rules.model.Rule;
import org.cogroo.tools.checker.rules.model.Rules;
import org.cogroo.util.Closeables;
import org.xml.sax.SAXException;
/**
* Class that provides access to the rules read from a xml file.
*/
public class RulesXmlAccess implements RulesAccess {
// private static final Logger LOGGER = Logger.getLogger(RulesXmlAccess.class);
private static RulesXmlAccess instance;
private URL schemaName;
private Schema schema;
private URL xmlFile;
private String serializedRule;
private RulesXmlAccess() {
}
/**
* Instantiates the singleton and recovers all rules from the xml file.
*/
private RulesXmlAccess(URL xmlFile, URL schemaFile) {
this.xmlFile = xmlFile;
this.schemaName = schemaFile;
this.loadSchema();
}
private RulesXmlAccess(String serializedRule, URL schemaFile) {
this.serializedRule = serializedRule;
this.schemaName = schemaFile;
this.loadSchema();
}
public static synchronized RulesAccess getInstance() {
if(instance == null) {
URL xml = RulesXmlAccess.class.getClassLoader().getResource("rules/rules.xml");
URL xsd = RulesXmlAccess.class.getClassLoader().getResource("rules/schema/rules.xsd");
instance = new RulesXmlAccess(xml, xsd);
}
return instance;
}
public static synchronized RulesAccess getInstance(String serializedRule) {
URL xsd = RulesXmlAccess.class.getClassLoader().getResource("rules/schema/rules.xsd");
return new RulesXmlAccess(serializedRule, xsd);
}
private void loadSchema() {
if (this.schema == null) {
InputStream in = null;
try {
in = this.schemaName.openStream();
StreamSource ss = new StreamSource(in);
SchemaFactory sf = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
this.schema = sf.newSchema(ss);
} catch (SAXException saxe) {
this.schema = null;
throw new RulesException("Could not read file " + this.schemaName, saxe);
} catch (IOException e) {
throw new RulesException("Could not open schema " + this.schemaName, e);
} finally {
Closeables.closeQuietly(in);
}
}
}
/**
* Checks the xml against the xsd.
*/
public void validate() {
Validator validator = this.schema.newValidator();
InputStream in = null;
try {
if(this.xmlFile != null) {
in = this.xmlFile.openStream();
} else {
// TODO: check if we need to specify the encoding
in = new ByteArrayInputStream(this.serializedRule.getBytes()) ;
}
validator.validate(new StreamSource(in));
} catch (SAXException e) {
throw new RulesException("Rules file does not conform to the schema", e);
} catch (IOException e) {
if(this.xmlFile != null) {
throw new RulesException("Could not read file " + this.xmlFile, e);
} else {
throw new RulesException("Could not read serialized rule " + this.serializedRule, e);
}
} finally {
Closeables.closeQuietly(in);
}
}
/**
* Gets a rule by its id.
*
* @param id
* the id of the rule
* @param rereadRules
* states if the rules must be read from the file or to reuse the
* in-memory representation, if it already exists
* @return the rule
*/
public Rule getRule(int id) {
Rule returnRule = null;
for (Rule rule : this.getRules().getRule()) {
if (rule.getId() == id) {
returnRule = rule;
break;
}
}
return returnRule;
}
public Rules getRules() {
Rules rules = null;
InputStream in = null;
if(xmlFile != null){
try {
in = xmlFile.openStream();
rules = unmarshal(in);
} catch (JAXBException e) {
throw new RulesException("Invalid rules file: " + this.xmlFile, e);
} catch (FileNotFoundException e) {
throw new RulesException("Could not find rules file: " + this.xmlFile, e);
} catch (IOException e) {
throw new RulesException("Could not open file: " + this.xmlFile, e);
} finally {
Closeables.closeQuietly(in);
}
} else {
try {
in = new ByteArrayInputStream(this.serializedRule.getBytes()) ;
rules = unmarshal(in);
} catch (JAXBException e) {
throw new RulesException("Invalid serialized rules: " + this.serializedRule, e);
} finally {
Closeables.closeQuietly(in);
}
}
return rules;
}
private Rules unmarshal(InputStream inputStream) throws JAXBException {
String packageName = Rules.class.getPackage().getName();
JAXBContext jc = JAXBContext.newInstance(packageName, ObjectFactory.class.getClassLoader());
Unmarshaller u = jc.createUnmarshaller();
loadSchema();
u.setSchema(this.schema);
return (Rules) u.unmarshal(inputStream);
}
public void persist(Rules newRules) {
throw new UnsupportedOperationException(
"Method not implemented: RulesXMLAccess.persist");
}
}