/****************************************************************************** * Copyright (c) 2016 Oracle * 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: * Konstantin Komissarchik - initial implementation and ongoing maintenance * Ling Hao - [337232] Certain schema causes elements to be out of order in corresponding xml files ******************************************************************************/ package org.eclipse.sapphire.modeling.xml.schema; import static org.eclipse.sapphire.modeling.util.MiscUtil.normalizeToEmptyString; import java.util.List; import javax.xml.namespace.QName; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public class XmlElementDefinition extends XmlContentModel { private QName elementName; private QName contentModelName; private boolean isAbstract; private QName substitutionGroup; private List<XmlElementDefinition> substitutionList; protected XmlElementDefinition( final XmlDocumentSchema schema, final QName elementName, final QName contentModelName, final int minOccur, final int maxOccur ) { this( schema, elementName, contentModelName, minOccur, maxOccur, false, null ); } protected XmlElementDefinition( final XmlDocumentSchema schema, final QName elementName, final QName contentModelName, final int minOccur, final int maxOccur, final boolean isAbstract, final QName substitutionGroup ) { super( schema, minOccur, maxOccur ); this.elementName = elementName; this.contentModelName = contentModelName; this.isAbstract = isAbstract; this.substitutionGroup = substitutionGroup; } public QName getName() { return this.elementName; } public QName getContentModelName() { return this.contentModelName; } public boolean isAbstract() { return this.isAbstract; } public QName getSubstitutionGroup() { return this.substitutionGroup; } public List<XmlElementDefinition> getSubstitutionList() { return this.substitutionList; } public void setSubstitutionList(List<XmlElementDefinition> substitutionList) { this.substitutionList = substitutionList; } public XmlContentModel getContentModel() { final QName contentModelName = getContentModelName(); if( contentModelName == null ) { return null; } else { final String namespace = contentModelName.getNamespaceURI(); final String localName = contentModelName.getLocalPart(); if( namespace.equals( normalizeToEmptyString( this.schema.getNamespace() ) ) ) { return this.schema.getContentModel( localName ); } else { final String importedSchemaLocation = this.schema.getSchemaLocation( namespace ); final XmlDocumentSchema importedSchema = XmlDocumentSchemasCache.getSchema( importedSchemaLocation ); return importedSchema.getContentModel( localName ); } } } @Override public XmlContentModel findChildElementContentModel( final QName childElementName ) { if( this.elementName.equals( childElementName ) ) { return getContentModel(); } return null; } @Override protected InsertionPosition findInsertionPosition( final NodeList nodeList, final int nodeListLength, final QName qname, final Position position ) { int elementsConsumed = 0; while( position.listIndex < nodeListLength ) { final Node node = nodeList.item( position.listIndex ); if( node.getNodeType() != Node.ELEMENT_NODE ) { position.listIndex++; } else { final QName eln = new QName( node.getNamespaceURI(), node.getLocalName() ); if ( sameElementName( eln ) ) { elementsConsumed++; position.listIndex++; } else { break; } } } final InsertionPosition result = new InsertionPosition(); if( sameElementName( qname ) ) { result.listIndex = position.listIndex; final int elementCountAfterAdd = elementsConsumed + 1; if( this.maxOccur != -1 && elementCountAfterAdd > this.maxOccur ) { result.grade = InsertionPosition.G1_EXCEEDS_MAX_OCCUR; } else if( elementCountAfterAdd <= this.minOccur ) { result.grade = InsertionPosition.G4_MEETS_OCCUR_REQUIREMENT; } else { result.grade = InsertionPosition.G3_OK_TO_INSERT; } } return result; } protected boolean sameElementName(QName qname) { if (this.elementName.equals( qname )) { return true; } return false; } @Override protected void toString( final StringBuilder buf, final String indent ) { buf.append( indent ); buf.append( "element [" ); //$NON-NLS-1$ buf.append( this.minOccur ); buf.append( ':' ); buf.append( this.maxOccur ); buf.append( "]\n" ); //$NON-NLS-1$ buf.append( indent ); buf.append( "{\n" ); //$NON-NLS-1$ buf.append( indent ); buf.append( " name = " ); //$NON-NLS-1$ buf.append( this.elementName ); buf.append( '\n' ); final QName contentModelName = getContentModelName(); if( contentModelName != null ) { buf.append( indent ); buf.append( " type = " ); //$NON-NLS-1$ buf.append( contentModelName ); buf.append( '\n' ); } if ( isAbstract() ) { buf.append( indent ); buf.append( " abstract=\"true\" "); //$NON-NLS-1$ buf.append( '\n' ); } if ( getSubstitutionGroup() != null ) { buf.append( indent ); buf.append( " SubstitutionGroup = " ); //$NON-NLS-1$ buf.append( getSubstitutionGroup() ); buf.append( '\n' ); } buf.append( indent ); buf.append( '}' ); } public static class Factory extends XmlContentModel.Factory { protected QName elementName; private QName contentModelName; private boolean isAbstract = false; private QName substitutionGroup = null; public final QName getName() { return this.elementName; } public final void setName( final QName elementName ) { this.elementName = elementName; } public final void setName( final String elementName ) { this.elementName = new QName( elementName ); } public QName getContentModelName() { return this.contentModelName; } public void setContentModelName( final QName contentModelName ) { this.contentModelName = contentModelName; } public void setContentModelName( final String contentModelName ) { this.contentModelName = new QName( contentModelName ); } public final boolean isAbstract() { return this.isAbstract; } public final void setAbstract(boolean isAbstract) { this.isAbstract = isAbstract; } public final QName getSubstitutionGroup() { return this.substitutionGroup; } public final void setSubstitutionGroup(QName substitutionGroup) { this.substitutionGroup = substitutionGroup; } @Override public XmlContentModel create( final XmlDocumentSchema schema ) { return new XmlElementDefinition( schema, this.elementName, this.contentModelName, this.minOccur, this.maxOccur, this.isAbstract, this.substitutionGroup ); } } }