Using enumeration to represent error messages legibily - is it good practice? - java

I'd like to consolidate my error messages and stuff into one file, and make my code more readable if possible.
Here's an example of what I have in my enum file:
public enum ZipErrorType {
// START: define exception messages (alphabetical order)
EMPTY_FILE_NAME_IN_LIST {
public String toString() {
return "One or more null/empty filename(s) found";
}
},
FILE_DOESNT_EXIST {
public String who(String sThisFile) {
return "[" + sThisFile + "] does not exist";
}
},
FILE_LIST_IS_NULL {
public String toString() {
return "File list is null/empty";
}
},
FILENAME_NOT_ABSOLUTE {
public String who(String sThisFile) {
return "[" + sThisFile + "] is not absolute";
}
},
MUST_BE_DIR {
public String who(String sThisFile) {
return "[" + sThisFile + "] must be a directory";
}
},
MUST_BE_FILE {
public String who(String sThisFile) {
return "[" + sThisFile + "] must be a file";
}
},
NULL_OR_EMPTY {
public String who(String sThisFile) {
return "[" + sThisFile + "] is null/empty";
}
},
OUTPUT_FILE_ALREADY_EXISTS {
public String who(String sThisFile) {
return "[" + sThisFile + "] already exists";
}
},
OUTPUT_FILENAME_EMPTY {
public String toString() {
return "Output filename is null/empty";
}
},
OUTPUT_PATH_EMPTY {
public String toString() {
return "Output path is null/empty";
}
},
// END: define exception messages
NONE {};
public String who(String sThisFile) { return ""; }
}
Then in my program I have code like:
private static ZipErrorType getFileErrorsIfAny(String sFilename, boolean shouldBeFile) {
// check if given filename is absolute
File file = new File(sFilename);
if (!file.isAbsolute()) {
return ZipErrorType.FILENAME_NOT_ABSOLUTE;
}
// check if file exists
if (!file.exists()) {
return ZipErrorType.FILE_DOESNT_EXIST;
}
// check if corresponding file is a file when it shouldn't be...
if (file.isFile() && !shouldBeFile) {
return ZipErrorType.MUST_BE_DIR;
}
// ...or a directory when it should be a file
else if (file.isDirectory() && shouldBeFile) {
return ZipErrorType.MUST_BE_FILE;
}
return ZipErrorType.NONE;
}
...and an example of how I make use of my enum:
// check input files
for (String sFile : files) {
if (sFile == null || sFile.trim().length() == 0) {
throw new NullPointerException("One or more filename is null/empty");
}
errorIfAny = getFileErrorsIfAny(sFile.trim(), true);
if (!errorIfAny.equals(ZipErrorType.NONE)) {
throw new ZipInputException(errorIfAny.who(sFile.trim()));
}
}
Now I know it's hard to judge just by these code snippets alone, but is this alright, from a general perspective? Is what I'm doing not worth the trouble, and is there a way to improve this?

I would suggest using simple string templates instead of enums for building error messages.
Something like this:
String EMPTY_FILE_NAME_IN_LIST_TEMPLATE = "One or more null/empty filename(s) found";
String FILE_DOESNT_EXIST_TEMPLATE = "[ %s ] does not exist";
String FILE_LIST_IS_NULL_TEMPLATE = "File list is null/empty";
String FILENAME_NOT_ABSOLUTE_TEMPLATE = "[ %s ] is not absolute";
String MUST_BE_DIR_TEMPLATE = "[ %s ] must be a directory";
String MUST_BE_FILE_TEMPLATE = "[ %s ] must be a file";
String NULL_OR_EMPTY_TEMPLATE = "[ %s ] is null/empty";
String OUTPUT_FILE_ALREADY_EXISTS_TEMPLATE = "[ %s ] already exists";
String OUTPUT_FILENAME_EMPTY_TEMPLATE = "Output filename is null/empty";
String OUTPUT_PATH_EMPTY_TEMPLATE = "Output path is null/empty";
And then, use String.format(template, sFilename) for building actual message.
You may also consider throwing an exception right out of getFileErrorsIfAny() method:
File file = new File(sFilename);
if (!file.isAbsolute()) {
throw new ZipInputException(String.format(FILENAME_NOT_ABSOLUTE_TEMPLATE, sFilename));
}
Looks cleaner and more compact to me.

This seems to have the potential to result in many many massive enums dotted around the code.
This isn't the first time someone has wanted to separate of the log message from the log statement.
In fact java.util.logging already has a framework for this that is designed for localisation.
It uses a .properties file which contains the messages.
You get the logger with the path to the file in the classpath : -
Logger logger = Logger.getLogger("com.example", "path/to/messages.properties");
Logging statements are then done using the property keys
logger.log(level, "messageKey");
And you can parameterise the logging because it uses MessageFormat syntax
zip.fileDoesNotExist={0} does not exist
logger.log(level, "zip.fileDoesNotExist", file);
These parameters are extremely flexible as you can specify formatting information in them and even use ChoiceFormat if needed.
The main advantage of all this is that your messages are in a separate file, rather than a class. And you can turn logging on and off at will with the logging.properties file. You can even turn logging on and off for single classes. And you can log to multiple files, to the console, you can send emails on errors etc etc
So, in conclusion. Use an existing logging framework. Don't roll your own.
Disclaimer: I only talk about JUL because then is built into Java - you don't need any 3rd party libs, there are many, many other frameworks out there.

Related

JSON Formatting in Data Logging File

I am trying to convert a file that contains data logging parameters to be json format.
Currently it retains the format:
{ X }
{ X }
I know this is not a valid JSON format, so I am trying to make it like this:
[
{ X },
{ X }
]
This is the code I have right know that logs data as this function is called:
public void write(String _rec) {
writeLock.writeLock().lock();
timestamp = System.currentTimeMillis();
String jsonstr = "{ \"timestamp\":" + timestamp + ", \"record\" : " + _rec + " },\n";
// Log.d(LOG_TAG, jsonstr);
try {
fileWriter.write(jsonstr);
fileWriter.flush();
} catch(IOException e) {
e.printStackTrace();
}
writeLock.writeLock().unlock();
} // END write
I added the comma in after the closing }, but I am not sure what to do about the []. Is there a way when I make the file I can insert the [] and then start writing information between them? Is there another solution to automatically insert these brackets?

How to get a specific value from a string name via config

The green is the lore
and the yellow is the displayname
http://puu.sh/k2iI7/62619f9536.jpg
I'm trying to seperate them in there rightful places for some odd reason there both appearing in both of the places.
items.java
public ItemStack applyLore(ItemStack stack, String name, String lore1){
ItemMeta meta = stack.getItemMeta();
meta.setDisplayName(name.replaceAll("&([0-9a-f])", "\u00A7$1"));
ArrayList<String> lore = new ArrayList<String>();
lore.add(lore1.replaceAll("&([0-9a-f])", "\u00A7$1"));
meta.setLore(lore);
stack.setItemMeta(meta);
return stack;
}
// p.getInventory().addItem(new ItemStack(Integer.parseInt(s), 1));
public void giveItemfromConfig(Player p)
{
String name ="name:";
String lore ="lore:";
for ( String s : plugin.file.getFile().getStringList(plugin.file.path) ) {
try {
s.split(" ");
if ( s.contains(name) || s.contains(lore) )
{
String namelength = s.substring(name.length());
String lorelength = s.substring(lore.length());
p.getInventory().addItem(applyLore(new ItemStack(Integer.parseInt(s.split(" ")[0])),
namelength.replace("_", " ").replace("ame:", "").replace("e:", "").replace("lor", "").replace("ore", ""),
lorelength.replace("_", " ").replace("lor:", "").replace("e:", "").replace("am", "").replace("lor", "").replace("ore", "")));
p.sendMessage("debug");
} else {
///nope.exe
p.getInventory().addItem(new ItemStack(Integer.parseInt(s)));
}
} catch(Exception e)
{
e.printStackTrace();
Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA + "Error in Config, Your id must be a integer ERROR:" + e);
}
}
}
config.yml
ChestPopulater:
items:
- 276 name:cookie
First thing is the problem is because of "Integer.parseInt(s)".
I dont know why you added this one as we dont have full source code or idea what you are trying to do with String "s" so that you will get result.
If you are doing it to get "276" , I will suggest you to do following :
String s2 = s.replaceAll("[A-Za-z]","").replace(":","").trim();
Integer i = Integer.parseInt(s2);

Include parameters for spring shell before method

I am working on a Spring Shell project. The tool is a command line tool to manipulate data in a database. There are commands like add user (which adds a record to a table in database). In order to execute any commands the user of the tool has to be connected to the database. I would like to be able to run this all in one line. The user of my tool should be able to write a command like the following.
--database connection string xyz --username abc --password mno add user --username bob --role AA_ADMIN --company Microsoft
Here the three parameters database connection string, username and password are required to run the add user command.
Below I have included some sample code it is from the spring shell reference docs
package commands;
import org.springframework.shell.core.CommandMarker;
import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import org.springframework.stereotype.Component;
#Component
public class UserManipulation implements CommandMarker {
private boolean simpleCommandExecuted = false;
#CliAvailabilityIndicator({"hw simple"})
public boolean isSimpleAvailable() {
//always available
return true;
}
#CliAvailabilityIndicator({"hw complex", "hw enum"})
public boolean isComplexAvailable() {
if (simpleCommandExecuted) {
return true;
} else {
return false;
}
}
#CliCommand(value = "hw simple", help = "Print a simple hello world message")
public String simple(
#CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final String message,
#CliOption(key = { "location" }, mandatory = false, help = "Where you are saying hello", specifiedDefaultValue="At work") final String location) {
simpleCommandExecuted = true;
return "Message = [" + message + "] Location = [" + location + "]";
}
#CliCommand(value = "hw complex", help = "Print a complex hello world message")
public String hello(
#CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final String message,
#CliOption(key = { "name1"}, mandatory = true, help = "Say hello to the first name") final String name1,
#CliOption(key = { "name2" }, mandatory = true, help = "Say hello to a second name") final String name2,
#CliOption(key = { "time" }, mandatory = false, specifiedDefaultValue="now", help = "When you are saying hello") final String time,
#CliOption(key = { "location" }, mandatory = false, help = "Where you are saying hello") final String location) {
return "Hello " + name1 + " and " + name2 + ". Your special message is " + message + ". time=[" + time + "] location=[" + location + "]";
}
#CliCommand(value = "hw enum", help = "Print a simple hello world message from an enumerated value")
public String eenum(
#CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final MessageType message){
return "Hello. Your special enumerated message is " + message;
}
enum MessageType {
Type1("type1"),
Type2("type2"),
Type3("type3");
private String type;
private MessageType(String type){
this.type = type;
}
public String getType(){
return type;
}
}
}
So currently, hw simple is a command that is required to be executed before running hw complex or hw enum command. I do not want hw simple to be a command instead it the message parameter within the hw simple command should be a parameter that is required as a prerequisite to run hw complex or hw enum. So for example the command that I would like to run is.
--message hw complex --message abc --name1 def --name2 ghi --time 7:98 --location: Seattle
Does anyone know how to do this? If it is not possible to do this I would like to hear that or any alternative ideas if possible.
You have 2 options here:
either make those 3 additional parameters (database, username, password) parameters of each and every command that require them (note that in your particular example, you would need to rename one of those username parameters [the one to connect to the DB, or the one that represents the user to add] as you can't have 2 parameters with the same name obviously).
Use the #CliAvailabilityIndicator approach, similar to what is described in the example, where a first command (maybe named use or connect) first tests the connection with the 3 given parameters and stores them somewhere, so that any further "real" command (such as add user) can use those values.
Also note that you can actually use a combination of the two (i.e. use solution 2 to provide defaults, that may be overridden on a case by case basis by solution 1).
Lastly, please note that you'll never be able to have something like what you describe at the beginning of your question, as command names must be at the beginning and they can't contain -- (options do)

Parsing XML with StAX with non-unique tag paths, design suggestions

I need to parse a large XML file (probably going to use StAX in Java) and output it into a delimited text file and I have a couple of design questions. First here is an example of the XML
<demographic>
<value>001</value>
<question>Name?</question>
<value>Bob</value>
<question>Last Name?</question>
<value>Smith</value>
<followUpQuestions>
<question>Middle Init.</question>
<value>J</value>
</followUpQuestions>
</demographic>
this would need to be outputted (in the delimited output file) as
001~Bob~Smith~J
so here are my questions:
How can I distinguish between all the different "value" tags, since the tag names are not unique. Currently I tried to resolve this by having 'state' variables that turn on once they pass question-text such as "Name?", however this approach doesnt really work for the first value since I have to check to make sure the 'name' and 'lastName' states are off to ensure I'm getting the first value.
Everytime the client changes the text of the questions (which happens) I have to change the code and recompile it. Is there anyway to avoid this? Maybe save the questions-text in a text file that the program reads in?
Can this be scalable? I need to extract over 100 values and the XML files are usually about 2 gigs large.
Thank you, in advance, for your help (from a Java and XML newbie)!!
UPDATE: here is my attempt to code the solution, can someone please help to streamline? There has to be a less messy way to do this:
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
class TestJavaForStackOverflow{
boolean nameState = false,
lastNameState = false,
middleInitState = false;
String name = "",
lastName = "",
middleInit = "",
value = "";
public void parse() throws IOException, XMLStreamException{
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader streamReader = factory.createXMLStreamReader(
new FileReader("/n04/data/revmgmt/anthony/scripts/Java_Programs/TestJavaForStackOverflow.xml"));
while(streamReader.hasNext()){
streamReader.next();
if(streamReader.getEventType() == XMLStreamReader.START_ELEMENT){
if("demographic".equals(streamReader.getLocalName())){
parseDemographicInformation(streamReader);
}
}
}
System.out.println(value + "~" + name + "~" + lastName + "~" + middleInit);
}
public void parseDemographicInformation(XMLStreamReader streamReader) throws XMLStreamException {
while(streamReader.hasNext()){
streamReader.next();
if(streamReader.getEventType() == XMLStreamReader.END_ELEMENT){
if("demographic".equals(streamReader.getLocalName())){
return;
}
}
else if(streamReader.getEventType() == XMLStreamReader.START_ELEMENT){
if("question".equals(streamReader.getLocalName())){
streamReader.next();
if("Name?".equals(streamReader.getText())){
nameState = true;
}
else if("Last Name?".equals(streamReader.getText())){
lastNameState = true;
}
else if("Middle Init.".equals(streamReader.getText())){
middleInitState = true;
}
}
else if("value".equals(streamReader.getLocalName())){
streamReader.next();
if(nameState){
name = streamReader.getText();
nameState = false;
}
else if (lastNameState){
lastName = streamReader.getText();
lastNameState = false;
}
else if (middleInitState){
middleInit = streamReader.getText();
middleInitState = false;
}
else {
value = streamReader.getText();
}
}
}
}
}
public static void main(String[] args){
TestJavaForStackOverflow t = new TestJavaForStackOverflow();
try{t.parse();}
catch(IOException e1){}
catch(XMLStreamException e2){}
}
}
I think the flags are not very scalable if you have a lot of different questions to parse, and neither are the global variables to hold the results... if you have 100 questions then you'll need 100 variables, and when they change over time it will be a bear to keep them up to date. I would use a map structure to hold the result, and another one to hold the correspondence between each question text and the corresponding field you are trying to capture (this is not actual Java, just an approximation):
public Map parseDemographicInformation(XmlStream xml, Map questionMap) {
Map record = new Map();
String field = "id";
while((elem = xml.getNextElement())) {
if(elem.tagName == "question") {
field = questionMap[elem.value];
} else if(elem.tagName == "value") {
record[field] = elem.value;
}
}
return record;
}
Then you have something like this to output the result:
String[] fieldsToOutput = { "id", "firstName", "lastName" }; // ideally read this from a file too so it can be changed dynamically
// ...
for(int i=0; i < fieldsToOutput.length; i++){
if(i > 0)
System.out.print("~");
System.out.print(record[fieldsToOutput[i]]);
}
System.out.println();

In Java, how do I parse an xml schema (xsd) to learn what's valid at a given element?

I'd like to be able to read in an XML schema (i.e. xsd) and from that know what are valid attributes, child elements, values as I walk through it.
For example, let's say I have an xsd that this xml will validate against:
<root>
<element-a type="something">
<element-b>blah</element-b>
<element-c>blahblah</element-c>
</element-a>
</root>
I've tinkered with several libraries and I can confidently get <root> as the root element. Beyond that I'm lost.
Given an element I need to know what child elements are required or allowed, attributes, facets, choices, etc. Using the above example I'd want to know that element-a has an attribute type and may have children element-b and element-c...or must have children element-b and element-c...or must have one of each...you get the picture I hope.
I've looked at numerous libraries such as XSOM, Eclipse XSD, Apache XmlSchema and found they're all short on good sample code. My search of the Internet has also been unsuccessful.
Does anyone know of a good example or even a book that demonstrates how to go through an XML schema and find out what would be valid options at a given point in a validated XML document?
clarification
I'm not looking to validate a document, rather I'd like to know the options at a given point to assist in creating or editing a document. If I know "I am here" in a document, I'd like to determing what I can do at that point. "Insert one of element A, B, or C" or "attach attribute 'description'".
This is a good question. Although, it is old, I did not find an acceptable answer. The thing is that the existing libraries I am aware of (XSOM, Apache XmlSchema) are designed as object models. The implementors did not have the intention to provide any utility methods — you should consider implement them yourself using the provided object model.
Let's see how querying context-specific elements can be done by the means of Apache XmlSchema.
You can use their tutorial as a starting point. In addition, Apache CFX framework provides the XmlSchemaUtils class with lots of handy code examples.
First of all, read the XmlSchemaCollection as illustrated by the library's tutorial:
XmlSchemaCollection xmlSchemaCollection = new XmlSchemaCollection();
xmlSchemaCollection.read(inputSource, new ValidationEventHandler());
Now, XML Schema defines two kinds of data types:
Simple types
Complex types
Simple types are represented by the XmlSchemaSimpleType class. Handling them is easy. Read the documentation: https://ws.apache.org/commons/XmlSchema/apidocs/org/apache/ws/commons/schema/XmlSchemaSimpleType.html. But let's see how to handle complex types. Let's start with a simple method:
#Override
public List<QName> getChildElementNames(QName parentElementName) {
XmlSchemaElement element = xmlSchemaCollection.getElementByQName(parentElementName);
XmlSchemaType type = element != null ? element.getSchemaType() : null;
List<QName> result = new LinkedList<>();
if (type instanceof XmlSchemaComplexType) {
addElementNames(result, (XmlSchemaComplexType) type);
}
return result;
}
XmlSchemaComplexType may stand for both real type and for the extension element. Please see the public static QName getBaseType(XmlSchemaComplexType type) method of the XmlSchemaUtils class.
private void addElementNames(List<QName> result, XmlSchemaComplexType type) {
XmlSchemaComplexType baseType = getBaseType(type);
XmlSchemaParticle particle = baseType != null ? baseType.getParticle() : type.getParticle();
addElementNames(result, particle);
}
When you handle XmlSchemaParticle, consider that it can have multiple implementations. See: https://ws.apache.org/commons/XmlSchema/apidocs/org/apache/ws/commons/schema/XmlSchemaParticle.html
private void addElementNames(List<QName> result, XmlSchemaParticle particle) {
if (particle instanceof XmlSchemaAny) {
} else if (particle instanceof XmlSchemaElement) {
} else if (particle instanceof XmlSchemaGroupBase) {
} else if (particle instanceof XmlSchemaGroupRef) {
}
}
The other thing to bear in mind is that elements can be either abstract or concrete. Again, the JavaDocs are the best guidance.
Many of the solutions for validating XML in java use the JAXB API. There's an extensive tutorial available here. The basic recipe for doing what you're looking for with JAXB is as follows:
Obtain or create the XML schema to validate against.
Generate Java classes to bind the XML to using xjc, the JAXB compiler.
Write java code to:
Open the XML content as an input stream.
Create a JAXBContext and Unmarshaller
Pass the input stream to the Unmarshaller's unmarshal method.
The parts of the tutorial you can read for this are:
Hello, world
Unmarshalling XML
I see you have tried Eclipse XSD. Have you tried Eclipse Modeling Framework (EMF)? You can:
Generating an EMF Model using XML Schema (XSD)
Create a dynamic instance from your metamodel (3.1 With the dynamic instance creation tool)
This is for exploring the xsd. You can create the dynamic instance of the root element then you can right click the element and create child element. There you will see what the possible children element and so on.
As for saving the created EMF model to an xml complied xsd: I have to look it up. I think you can use JAXB for that (How to use EMF to read XML file?).
Some refs:
EMF: Eclipse Modeling Framework, 2nd Edition (written by creators)
Eclipse Modeling Framework (EMF)
Discover the Eclipse Modeling Framework (EMF) and Its Dynamic Capabilities
Creating Dynamic EMF Models From XSDs and Loading its Instances From XML as SDOs
This is a fairly complete sample on how to parse an XSD using XSOM:
import java.io.File;
import java.util.Iterator;
import java.util.Vector;
import org.xml.sax.ErrorHandler;
import com.sun.xml.xsom.XSComplexType;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSFacet;
import com.sun.xml.xsom.XSModelGroup;
import com.sun.xml.xsom.XSModelGroupDecl;
import com.sun.xml.xsom.XSParticle;
import com.sun.xml.xsom.XSRestrictionSimpleType;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.XSSimpleType;
import com.sun.xml.xsom.XSTerm;
import com.sun.xml.xsom.impl.Const;
import com.sun.xml.xsom.parser.XSOMParser;
import com.sun.xml.xsom.util.DomAnnotationParserFactory;
public class XSOMNavigator
{
public static class SimpleTypeRestriction
{
public String[] enumeration = null;
public String maxValue = null;
public String minValue = null;
public String length = null;
public String maxLength = null;
public String minLength = null;
public String[] pattern = null;
public String totalDigits = null;
public String fractionDigits = null;
public String whiteSpace = null;
public String toString()
{
String enumValues = "";
if (enumeration != null)
{
for(String val : enumeration)
{
enumValues += val + ", ";
}
enumValues = enumValues.substring(0, enumValues.lastIndexOf(','));
}
String patternValues = "";
if (pattern != null)
{
for(String val : pattern)
{
patternValues += "(" + val + ")|";
}
patternValues = patternValues.substring(0, patternValues.lastIndexOf('|'));
}
String retval = "";
retval += minValue == null ? "" : "[MinValue = " + minValue + "]\t";
retval += maxValue == null ? "" : "[MaxValue = " + maxValue + "]\t";
retval += minLength == null ? "" : "[MinLength = " + minLength + "]\t";
retval += maxLength == null ? "" : "[MaxLength = " + maxLength + "]\t";
retval += pattern == null ? "" : "[Pattern(s) = " + patternValues + "]\t";
retval += totalDigits == null ? "" : "[TotalDigits = " + totalDigits + "]\t";
retval += fractionDigits == null ? "" : "[FractionDigits = " + fractionDigits + "]\t";
retval += whiteSpace == null ? "" : "[WhiteSpace = " + whiteSpace + "]\t";
retval += length == null ? "" : "[Length = " + length + "]\t";
retval += enumeration == null ? "" : "[Enumeration Values = " + enumValues + "]\t";
return retval;
}
}
private static void initRestrictions(XSSimpleType xsSimpleType, SimpleTypeRestriction simpleTypeRestriction)
{
XSRestrictionSimpleType restriction = xsSimpleType.asRestriction();
if (restriction != null)
{
Vector<String> enumeration = new Vector<String>();
Vector<String> pattern = new Vector<String>();
for (XSFacet facet : restriction.getDeclaredFacets())
{
if (facet.getName().equals(XSFacet.FACET_ENUMERATION))
{
enumeration.add(facet.getValue().value);
}
if (facet.getName().equals(XSFacet.FACET_MAXINCLUSIVE))
{
simpleTypeRestriction.maxValue = facet.getValue().value;
}
if (facet.getName().equals(XSFacet.FACET_MININCLUSIVE))
{
simpleTypeRestriction.minValue = facet.getValue().value;
}
if (facet.getName().equals(XSFacet.FACET_MAXEXCLUSIVE))
{
simpleTypeRestriction.maxValue = String.valueOf(Integer.parseInt(facet.getValue().value) - 1);
}
if (facet.getName().equals(XSFacet.FACET_MINEXCLUSIVE))
{
simpleTypeRestriction.minValue = String.valueOf(Integer.parseInt(facet.getValue().value) + 1);
}
if (facet.getName().equals(XSFacet.FACET_LENGTH))
{
simpleTypeRestriction.length = facet.getValue().value;
}
if (facet.getName().equals(XSFacet.FACET_MAXLENGTH))
{
simpleTypeRestriction.maxLength = facet.getValue().value;
}
if (facet.getName().equals(XSFacet.FACET_MINLENGTH))
{
simpleTypeRestriction.minLength = facet.getValue().value;
}
if (facet.getName().equals(XSFacet.FACET_PATTERN))
{
pattern.add(facet.getValue().value);
}
if (facet.getName().equals(XSFacet.FACET_TOTALDIGITS))
{
simpleTypeRestriction.totalDigits = facet.getValue().value;
}
if (facet.getName().equals(XSFacet.FACET_FRACTIONDIGITS))
{
simpleTypeRestriction.fractionDigits = facet.getValue().value;
}
if (facet.getName().equals(XSFacet.FACET_WHITESPACE))
{
simpleTypeRestriction.whiteSpace = facet.getValue().value;
}
}
if (enumeration.size() > 0)
{
simpleTypeRestriction.enumeration = enumeration.toArray(new String[] {});
}
if (pattern.size() > 0)
{
simpleTypeRestriction.pattern = pattern.toArray(new String[] {});
}
}
}
private static void printParticle(XSParticle particle, String occurs, String absPath, String indent)
{
boolean repeats = particle.isRepeated();
occurs = " MinOccurs = " + particle.getMinOccurs() + ", MaxOccurs = " + particle.getMaxOccurs() + ", Repeats = " + Boolean.toString(repeats);
XSTerm term = particle.getTerm();
if (term.isModelGroup())
{
printGroup(term.asModelGroup(), occurs, absPath, indent);
}
else if(term.isModelGroupDecl())
{
printGroupDecl(term.asModelGroupDecl(), occurs, absPath, indent);
}
else if (term.isElementDecl())
{
printElement(term.asElementDecl(), occurs, absPath, indent);
}
}
private static void printGroup(XSModelGroup modelGroup, String occurs, String absPath, String indent)
{
System.out.println(indent + "[Start of Group " + modelGroup.getCompositor() + occurs + "]" );
for (XSParticle particle : modelGroup.getChildren())
{
printParticle(particle, occurs, absPath, indent + "\t");
}
System.out.println(indent + "[End of Group " + modelGroup.getCompositor() + "]");
}
private static void printGroupDecl(XSModelGroupDecl modelGroupDecl, String occurs, String absPath, String indent)
{
System.out.println(indent + "[GroupDecl " + modelGroupDecl.getName() + occurs + "]");
printGroup(modelGroupDecl.getModelGroup(), occurs, absPath, indent);
}
private static void printComplexType(XSComplexType complexType, String occurs, String absPath, String indent)
{
System.out.println();
XSParticle particle = complexType.getContentType().asParticle();
if (particle != null)
{
printParticle(particle, occurs, absPath, indent);
}
}
private static void printSimpleType(XSSimpleType simpleType, String occurs, String absPath, String indent)
{
SimpleTypeRestriction restriction = new SimpleTypeRestriction();
initRestrictions(simpleType, restriction);
System.out.println(restriction.toString());
}
public static void printElement(XSElementDecl element, String occurs, String absPath, String indent)
{
absPath += "/" + element.getName();
String typeName = element.getType().getBaseType().getName();
if(element.getType().isSimpleType() && element.getType().asSimpleType().isPrimitive())
{
// We have a primitive type - So use that instead
typeName = element.getType().asSimpleType().getPrimitiveType().getName();
}
boolean nillable = element.isNillable();
System.out.print(indent + "[Element " + absPath + " " + occurs + "] of type [" + typeName + "]" + (nillable ? " [nillable] " : ""));
if (element.getType().isComplexType())
{
printComplexType(element.getType().asComplexType(), occurs, absPath, indent);
}
else
{
printSimpleType(element.getType().asSimpleType(), occurs, absPath, indent);
}
}
public static void printNameSpace(XSSchema s, String indent)
{
String nameSpace = s.getTargetNamespace();
// We do not want the default XSD namespaces or a namespace with nothing in it
if(nameSpace == null || Const.schemaNamespace.equals(nameSpace) || s.getElementDecls().isEmpty())
{
return;
}
System.out.println("Target namespace: " + nameSpace);
Iterator<XSElementDecl> jtr = s.iterateElementDecls();
while (jtr.hasNext())
{
XSElementDecl e = (XSElementDecl) jtr.next();
String occurs = "";
String absPath = "";
XSOMNavigator.printElement(e, occurs, absPath,indent);
System.out.println();
}
}
public static void xsomNavigate(File xsdFile)
{
ErrorHandler errorHandler = new ErrorReporter(System.err);
XSSchemaSet schemaSet = null;
XSOMParser parser = new XSOMParser();
try
{
parser.setErrorHandler(errorHandler);
parser.setAnnotationParser(new DomAnnotationParserFactory());
parser.parse(xsdFile);
schemaSet = parser.getResult();
}
catch (Exception exp)
{
exp.printStackTrace(System.out);
}
if(schemaSet != null)
{
// iterate each XSSchema object. XSSchema is a per-namespace schema.
Iterator<XSSchema> itr = schemaSet.iterateSchema();
while (itr.hasNext())
{
XSSchema s = (XSSchema) itr.next();
String indent = "";
printNameSpace(s, indent);
}
}
}
public static void printFile(String fileName)
{
File fileToParse = new File(fileName);
if (fileToParse != null && fileToParse.canRead())
{
xsomNavigate(fileToParse);
}
}
}
And for your Error Reporter use:
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.MessageFormat;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class ErrorReporter implements ErrorHandler {
private final PrintStream out;
public ErrorReporter( PrintStream o ) { this.out = o; }
public ErrorReporter( OutputStream o ) { this(new PrintStream(o)); }
public void warning(SAXParseException e) throws SAXException {
print("[Warning]",e);
}
public void error(SAXParseException e) throws SAXException {
print("[Error ]",e);
}
public void fatalError(SAXParseException e) throws SAXException {
print("[Fatal ]",e);
}
private void print( String header, SAXParseException e ) {
out.println(header+' '+e.getMessage());
out.println(MessageFormat.format(" line {0} at {1}",
new Object[]{
Integer.toString(e.getLineNumber()),
e.getSystemId()}));
}
}
For your main use:
public class WDXSOMParser
{
public static void main(String[] args)
{
String fileName = null;
if(args != null && args.length > 0 && args[0] != null)
fileName = args[0];
else
fileName = "C:\\xml\\CollectionComments\\CollectionComment1.07.xsd";
//fileName = "C:\\xml\\PropertyListingContractSaleInfo\\PropertyListingContractSaleInfo.xsd";
//fileName = "C:\\xml\\PropertyPreservation\\PropertyPreservation.xsd";
XSOMNavigator.printFile(fileName);
}
}
It's agood bit of work depending on how compex your xsd is but basically.
if you had
<Document>
<Header/>
<Body/>
<Document>
And you wanted to find out where were the alowable children of header you'd (taking account of namespaces)
Xpath would have you look for '/element[name="Document"]/element[name="Header"]'
After that it depends on how much you want to do. You might find it easier to write or find something that loads an xsd into a DOM type structure.
Course you are going to possibly find all sorts of things under that elment in xsd, choice, sequence, any, attributes, complexType, SimpleContent, annotation.
Loads of time consuming fun.
Have a look at this.
How to parse schema using XOM Parser.
Also, here is the project home for XOM

Categories

Resources