/**
* $Id: $
* $Date: $
*
*/
package org.xmlsh.json;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import net.sf.saxon.s9api.BuildingStreamWriter;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XQueryCompiler;
import net.sf.saxon.s9api.XQueryEvaluator;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XdmValue;
import net.sf.saxon.trans.XPathException;
import org.apache.xerces.xs.XSAnnotation;
import org.apache.xerces.xs.XSAttributeDeclaration;
import org.apache.xerces.xs.XSAttributeUse;
import org.apache.xerces.xs.XSComplexTypeDefinition;
import org.apache.xerces.xs.XSConstants;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSModel;
import org.apache.xerces.xs.XSModelGroup;
import org.apache.xerces.xs.XSNamedMap;
import org.apache.xerces.xs.XSObject;
import org.apache.xerces.xs.XSObjectList;
import org.apache.xerces.xs.XSParticle;
import org.apache.xerces.xs.XSSimpleTypeDefinition;
import org.apache.xerces.xs.XSTerm;
import org.apache.xerces.xs.XSTypeDefinition;
import org.xml.sax.ContentHandler;
import org.xmlsh.core.CoreException;
import org.xmlsh.core.Options;
import org.xmlsh.core.XCommand;
import org.xmlsh.core.XEnvironment;
import org.xmlsh.core.XValue;
import org.xmlsh.core.XVariable;
import org.xmlsh.core.io.OutputPort;
import org.xmlsh.core.io.VariableOutputPort;
import org.xmlsh.schema.Schema;
import org.xmlsh.sh.shell.SerializeOpts;
import org.xmlsh.sh.shell.Shell;
import org.xmlsh.util.StAXUtils;
import org.xmlsh.util.Util;
public class jxon extends XCommand{
private static final String CONTEXT_TYPE = "type_decl";
private static final String CONTEXT_ATTRIBUTE = "attribute";
private static final String CONTEXT_ELEMENT = "element";
private static final String CONTEXT_ELEMENT_REF = "element_ref";
private static final String CONTEXT_DOCUMENT = "document";
private static final String JXON_NS = "http://www.xmlsh.org/jxon";
private XdmNode parse( XSAnnotation xanno , SerializeOpts opts ) throws XPathException, SaxonApiException, CoreException, IOException
{
XVariable var = XVariable.anonymousInstance( new XValue());
OutputPort out = new VariableOutputPort( var );
ContentHandler handler = out.asContentHandler(opts );
xanno.writeAnnotation(handler, XSAnnotation.SAX_CONTENTHANDLER);
out.flush();
out.close();
XdmValue xv = var.getValue().xpath( getShell() , "/xs:annotation/xs:appinfo/jxon:*").toXdmValue();
/*
* Reserialize with a namespacer reduicer
*/
var = XVariable.anonymousInstance(XValue.nullValue());
out = new VariableOutputPort( var );
Util.writeXdmValue(xv ,out.asDestination(opts ));
out.flush();
out.close();
return var.getValue().asXdmNode();
}
private class Annotation extends XValue
{
public Annotation( XSAnnotation xanno ) throws XPathException, SaxonApiException, CoreException, IOException{
super( parse( xanno , mSerializeOpts ) );
}
}
@SuppressWarnings("serial")
private static class AnnotationList extends ArrayList<Annotation>
{
public AnnotationList()
{
super();
}
public AnnotationList(int length) {
super(length);
}
/* (non-Javadoc)
* @see java.util.ArrayList#add(java.lang.Object)
*/
@Override
public boolean add(Annotation e) {
if( e == null || e.isNull() )
throw new RuntimeException("Invalid Null Annotation");
return super.add(e);
}
}
private class AnnotationEntry
{
QName mName; // required
String mContext; // required
XSTypeDefinition mType; // may be null
AnnotationList mAnnotation; // required
AnnotationEntryList mChildren; // optional
List<QName> mChildRefs; // optional
public AnnotationEntry(String context , QName name , AnnotationEntry parent , XSTypeDefinition type, AnnotationList annotation) {
super();
mName = name ;
mContext = context ;
mType = type;
mAnnotation = annotation;
if( parent != null )
parent.add( this );
}
private void add(AnnotationEntry annotationEntry) {
if( mChildren == null )
mChildren = new AnnotationEntryList();
mChildren.add(annotationEntry);
}
public void serialize( XMLStreamWriter sw ) throws SaxonApiException, XMLStreamException, XPathException, CoreException
{
sw.writeStartElement( "jxon", mContext , JXON_NS );
// sw.writeDefaultNamespace(JXON_NS);
XSSimpleTypeDefinition itemType = null ;
XSObjectList memberTypes = null ;
if( mType != null ){
sw.writeAttribute("typeCategory", getTypeCategory(mType));
if( mType.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE ){
XSComplexTypeDefinition ctd = (XSComplexTypeDefinition) mType ;
sw.writeAttribute( "contentType" , getTypeContentType( ctd ) );
if( mName.getLocalName().equals("value"))
sw.writeAttribute("abstract", ctd.getAbstract() ? "true" : "false" );
}
else
if( mType.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE ){
XSSimpleTypeDefinition std = (XSSimpleTypeDefinition) mType ;
sw.writeAttribute( "variety" , getVariety( std ) );
itemType = std.getItemType();
memberTypes = std.getMemberTypes();
}
}
if( ! mContext.equals(CONTEXT_DOCUMENT)){
writeQName( sw , "name" , mName );
if( mType != null ){
if( mContext.equals(CONTEXT_TYPE )){
QName baseType = getName(mType.getBaseType());
// Prevent infinate recursion
if( baseType != null && ! baseType.equals(mName))
writeQName( sw , "basetype" , baseType );
if( itemType != null )
writeQName( sw , "itemtype" , getName( itemType ));
if( memberTypes != null ){
for( int i = 0 ; i < memberTypes.getLength() ; i++ ){
XSSimpleTypeDefinition member = (XSSimpleTypeDefinition ) memberTypes.item(i);
writeQName( sw , "membertype" , getName( member ));
}
}
}
else
writeQName( sw , "type" , getName(mType) );
}
}
if( mAnnotation != null ){
for( Annotation a : mAnnotation ){
StAXUtils.copy( a.asNodeInfo() , sw );
}
}
if( mChildren != null ){
mChildren.serialize(sw);
}
if( mChildRefs != null ){
for( QName c : mChildRefs )
writeQName( sw , "child" , c );
}
sw.writeEndElement();
}
private String getVariety(XSSimpleTypeDefinition std) {
switch( std.getVariety()) {
case XSSimpleTypeDefinition.VARIETY_ABSENT :
return "absent" ;
case XSSimpleTypeDefinition.VARIETY_ATOMIC:
return "atomic" ;
case XSSimpleTypeDefinition.VARIETY_LIST :
return "list" ;
case XSSimpleTypeDefinition.VARIETY_UNION:
return "union" ;
default :
return "absent" ;
}
}
private String getTypeContentType(XSComplexTypeDefinition type) {
switch( type.getContentType() ){
case XSComplexTypeDefinition.CONTENTTYPE_ELEMENT :
return "element" ;
case XSComplexTypeDefinition.CONTENTTYPE_EMPTY :
return "empty" ;
case XSComplexTypeDefinition.CONTENTTYPE_MIXED :
return "mixed" ;
case XSComplexTypeDefinition.CONTENTTYPE_SIMPLE:
return "simple" ;
default:
return "";
}
}
private String getTypeCategory(XSTypeDefinition type) {
switch( type.getTypeCategory()){
case XSTypeDefinition.COMPLEX_TYPE :
return "complex";
case XSTypeDefinition.SIMPLE_TYPE :
return "simple" ;
default:
return "";
}
}
public void addChildReference(QName child) {
if( mChildRefs == null )
mChildRefs = new ArrayList<QName>();
mChildRefs.add( child );
}
};
@SuppressWarnings("serial")
private class AnnotationEntryList extends ArrayList<AnnotationEntry>
{
public void serialize(XMLStreamWriter sw ) throws SaxonApiException, XMLStreamException, XPathException, CoreException
{
for( AnnotationEntry a : this ){
a.serialize(sw);
}
}
}
private SerializeOpts mSerializeOpts ;
private XdmNode mPatterns;
private static int level = 0;
private Schema mSchema = null;
public int run(List<XValue> args) throws Exception {
Options opts = new Options("xsd:,o=output:,v,n",SerializeOpts.getOptionDefs());
opts.parse(args);
args = opts.getRemainingArgs();
mSerializeOpts = getSerializeOpts(opts);
String xsd = opts.getOptStringRequired("xsd");
String output = opts.getOptString("o", ".");
Shell shell = getShell();
shell.getEnv().declareNamespace("jxon",JXON_NS );
mSchema = new Schema( shell.getURI( xsd ).toString() );
URL patterns_url = mShell.getResource("/org/xmlsh/json/patterns/patterns.xml");
mPatterns = Util.asXdmNode( patterns_url);
AnnotationEntry docEntry = getGlobalAnnotations();
getTypeAnnotations( docEntry);
getElementAnnotations( docEntry );
File outputDir = shell.getExplicitFile(output,false);
if( ! outputDir.exists() )
outputDir.mkdirs();
XEnvironment env = shell.getEnv();
if( opts.hasOpt("v")){
XMLStreamWriter sw = this.getStdout().asXMLStreamWriter(mSerializeOpts);
serialize( sw , docEntry);
sw.close();
this.getStdout().flush();
}
if( ! opts.hasOpt("n")){
OutputPort xml_port = env.getOutput( shell.getExplicitFile( outputDir, "toxml.xsl" , false ),false);
OutputPort json_port = env.getOutput( shell.getExplicitFile( outputDir, "tojson.xsl" , false ), false);
BuildingStreamWriter b = Shell.getProcessor().newDocumentBuilder().newBuildingStreamWriter();
serialize( b , docEntry );
createXSLT( b.getDocumentNode() , "patterns/create_xml.xquery" , xml_port );
createXSLT( b.getDocumentNode() , "patterns/create_json.xquery" , json_port );
}
return 0;
}
private void serialize(XMLStreamWriter sw, AnnotationEntry entry ) throws XMLStreamException, SaxonApiException, XPathException, CoreException
{
entry.serialize(sw);
sw.flush();
}
private void createXSLT(XdmItem annos , String script , OutputPort xmlPort) throws SaxonApiException, IOException, CoreException, URISyntaxException {
XQueryExecutable exe = getXQuery(script);
XQueryEvaluator eval = exe.load();
// pass nodes as a sequence of items
eval.setDestination( xmlPort.asDestination(mSerializeOpts));
eval.setExternalVariable( new QName("http://www.xmlsh.org/jsonxml/common","annotations"), annos);
eval.setExternalVariable( new QName("http://www.xmlsh.org/jsonxml/common","patterns"), mPatterns );
eval.run();
}
private AnnotationEntry getGlobalAnnotations() throws XPathException,
XMLStreamException, CoreException, SaxonApiException, IOException {
XSObjectList annotations = mSchema.getAnnotations();
AnnotationList value = getAnnotations( annotations );
AnnotationEntry self = new AnnotationEntry( CONTEXT_DOCUMENT , null , null , null , value);
return self ;
}
private void getAnnotations(XSParticle particle, AnnotationEntry parent ) throws XPathException,
XMLStreamException, CoreException, SaxonApiException, IOException {
// if( level > 40)
// return ;
if( particle == null )
return ;
XSTerm term = particle.getTerm() ;
if( term == null )
return ;
level++;
switch( term.getType() ){
case XSConstants.ELEMENT_DECLARATION :
{
XSElementDeclaration child = (XSElementDeclaration) term ;
/*
* Only recurse for local elements
*/
if( child.getScope() == XSConstants.SCOPE_LOCAL )
getAnnotations( child , parent );
/*
* Reference to a child, add it as a reference
*/
else
addReference( parent , child );
/*
* If this is a member of a substitution group then add all other elements of the same group but dont recurse
*/
List<XSElementDeclaration> subs = getSubElements(child);
for( XSElementDeclaration e : subs ){
// XSTypeDefinition stype = e.getTypeDefinition();
QName selemName = getName(e);
new AnnotationEntry( CONTEXT_ELEMENT_REF , selemName, parent , null , null );
}
break ;
}
case XSConstants.MODEL_GROUP : ;
{
XSModelGroup group = (XSModelGroup) term ;
XSObjectList particles = group.getParticles();
for( int i = 0 ; i < particles.getLength() ; i++ ){
XSParticle p = (XSParticle) particles.item(i);
getAnnotations( p , parent );
}
break;
}
case XSConstants.WILDCARD :
break ;
}
level--;
}
private void addReference(AnnotationEntry parent, XSElementDeclaration child) {
parent.addChildReference( this.getName(child));
}
private void getElementAnnotations( AnnotationEntry parent ) throws XPathException,
XMLStreamException, CoreException, SaxonApiException, IOException {
XSModel model = mSchema.getModel();
XSNamedMap types = model.getComponents(XSConstants.ELEMENT_DECLARATION);
for( int i = 0 ; i < types.getLength() ; i++ ){
XSObject obj = types.item(i);
if( obj instanceof XSElementDeclaration )
getAnnotations((XSElementDeclaration)obj, parent );
}
}
private List<XSElementDeclaration> getSubElements(XSElementDeclaration that )
throws XPathException,
XMLStreamException, CoreException, SaxonApiException {
ArrayList<XSElementDeclaration> subs = new ArrayList<XSElementDeclaration>();
XSModel model = mSchema.getModel();
XSNamedMap types = model.getComponents(XSConstants.ELEMENT_DECLARATION);
for( int i = 0 ; i < types.getLength() ; i++ ){
XSObject obj = types.item(i);
if( obj instanceof XSElementDeclaration ){
XSElementDeclaration xed = (XSElementDeclaration) obj;
if( xed != that && xed.getSubstitutionGroupAffiliation() == that )
subs.add(xed);
}
}
return subs;
}
private void getAnnotations(XSElementDeclaration obj, AnnotationEntry parent ) throws XPathException,
XMLStreamException, CoreException, SaxonApiException, IOException {
{
AnnotationList annotation = getAnnotations( obj.getAnnotations() );
XSTypeDefinition type = obj.getTypeDefinition();
QName elemName = getName(obj);
boolean bAbstract = obj.getAbstract();
AnnotationEntry self = new AnnotationEntry( CONTEXT_ELEMENT , elemName, parent , type , annotation );
if( type instanceof XSComplexTypeDefinition ){
XSComplexTypeDefinition ctype = (XSComplexTypeDefinition) type ;
XSObjectList attrList = ctype.getAttributeUses();
/*
* Recurse to attributes
*/
for( int n = 0 ; n < attrList.getLength() ; n++ ){
XSObject attrobj = attrList.item(n);
if( attrobj instanceof XSAttributeUse ){
XSAttributeUse attrUse = (XSAttributeUse) attrobj ;
XSAttributeDeclaration attrDecl = attrUse.getAttrDeclaration();
AnnotationList useAnno = getAnnotations( attrUse.getAnnotations() );
type = attrDecl.getTypeDefinition() ;
AnnotationList declAnno = getAnnotations( attrDecl.getAnnotations() );
/*
* If there is both a use and a declaraition annotation then use the use
*
*/
new AnnotationEntry( CONTEXT_ATTRIBUTE , getName(attrDecl) , self , type , useAnno != null ? useAnno : declAnno );
}
}
/*
* Recuse to local elements
*/
XSParticle particle = ctype.getParticle();
getAnnotations( particle , self );
}
}
}
private void getTypeAnnotations( AnnotationEntry parent) throws XPathException, XMLStreamException, CoreException, SaxonApiException, IOException {
XSModel model = mSchema.getModel();
XSNamedMap types = model.getComponents(XSConstants.TYPE_DEFINITION);
for( int i = 0 ; i < types.getLength() ; i++ ){
XSObject obj = types.item(i);
if( obj instanceof XSTypeDefinition ){
AnnotationList value = null ;
XSTypeDefinition type = (XSTypeDefinition) obj ;
if( type instanceof XSComplexTypeDefinition )
value = getAnnotations( ((XSComplexTypeDefinition)type).getAnnotations() );
else
if( type instanceof XSSimpleTypeDefinition )
value = getAnnotations( ((XSSimpleTypeDefinition)type).getAnnotations() );
new AnnotationEntry( CONTEXT_TYPE, getName(type) , parent , type , value );
}
}
}
private QName getName(XSTypeDefinition type) {
if( type == null )
return null ;
String namespace = type.getNamespace();
String name = type.getName();
QName qname = new QName(Util.notNull(namespace), Util.notNull(name) );
return qname;
}
private QName getName(XSSimpleTypeDefinition type) {
if( type == null )
return null ;
String namespace = type.getNamespace();
String name = type.getName();
QName qname = new QName(Util.notNull(namespace), Util.notNull(name) );
return qname;
}
private QName getName(XSObject obj) {
QName qname = new QName(obj.getNamespace(), obj.getName() );
return qname;
}
private XQueryExecutable getXQuery(String string) throws SaxonApiException, IOException, URISyntaxException {
XQueryCompiler mXQueryCompiler = Shell.getProcessor().newXQueryCompiler();
InputStream isQuery = getClass().getResourceAsStream(string);
try {
URL url = mShell.getResource("/org/xmlsh/json/" + string );
if( url != null ){
URI uri = url.toURI();
mXQueryCompiler.setBaseURI( uri );
}
return mXQueryCompiler.compile( isQuery );
} finally {
isQuery.close();
}
}
private AnnotationList getAnnotations(XSObjectList annotations) throws XPathException, XMLStreamException, CoreException, SaxonApiException, IOException {
if( annotations == null || annotations.getLength() == 0 )
return null ;
AnnotationList annos = new AnnotationList( annotations.getLength() );
for( int i = 0 ; i < annotations.getLength() ; i++ ){
XSAnnotation annotation = (XSAnnotation) annotations.item(i);
annos.add(new Annotation(annotation));
}
return annos;
}
private void writeQName( XMLStreamWriter sw , String name , QName q ) throws XMLStreamException
{
if( q == null )
return ;
if( Util.isBlank(q.getLocalName()))
return ;
sw.writeStartElement("jxon" , name,JXON_NS );
// sw.writeAttribute("prefix", q.getPrefix() );
sw.writeAttribute("uri" , q.getNamespaceURI());
sw.writeAttribute("localname", q.getLocalName());
sw.writeEndElement();
}
}
//
//
//Copyright (C) 2008-2014 David A. Lee.
//
//The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php
//
//Software distributed under the License is distributed on an "AS IS" basis,
//WITHOUT WARRANTY OF ANY KIND, either express or implied.
//See the License for the specific language governing rights and limitations under the License.
//
//The Original Code is: all this file.
//
//The Initial Developer of the Original Code is David A. Lee
//
//Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
//
//Contributor(s): none.
//