/******************************************************************************
* 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
******************************************************************************/
package org.eclipse.sapphire.tests.modeling;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.eclipse.sapphire.modeling.xml.schema.XmlChoiceGroup;
import org.eclipse.sapphire.modeling.xml.schema.XmlContentModel;
import org.eclipse.sapphire.modeling.xml.schema.XmlElementDefinition;
import org.eclipse.sapphire.modeling.xml.schema.XmlSequenceGroup;
import org.eclipse.sapphire.tests.SapphireTestCase;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/
public final class FindInsertionPositionTests extends SapphireTestCase
{
@Test
public void test_1A() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 0, -1 ),
element( "B", 0, -1 ),
element( "C", 0, -1 )
).create( null );
final String start = "<root></root>";
final String expected = "<root><A/></root>";
test( XmlContentModel, start, "A", expected );
}
@Test
public void test_1B() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 0, -1 ),
element( "B", 0, -1 ),
element( "C", 0, -1 )
).create( null );
final String start = "<root></root>";
final String expected = "<root><B/></root>";
test( XmlContentModel, start, "B", expected );
}
@Test
public void test_1C() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 0, -1 ),
element( "B", 0, -1 ),
element( "C", 0, -1 )
).create( null );
final String start = "<root><A/><A/><B/><B/><C/><C/></root>";
final String expected = "<root><A/><A/><A/><B/><B/><C/><C/></root>";
test( XmlContentModel, start, "A", expected );
}
@Test
public void test_1D() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 0, -1 ),
element( "B", 0, -1 ),
element( "C", 0, -1 )
).create( null );
final String start = "<root><A/><A/><B/><B/><C/><C/></root>";
final String expected = "<root><A/><A/><B/><B/><B/><C/><C/></root>";
test( XmlContentModel, start, "B", expected );
}
@Test
public void test_1E() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 0, -1 ),
element( "B", 0, -1 ),
element( "C", 0, -1 )
).create( null );
final String start = "<root><A/><A/><B/><B/><C/><C/></root>";
final String expected = "<root><A/><A/><B/><B/><C/><C/><C/></root>";
test( XmlContentModel, start, "C", expected );
}
@Test
public void test_2A() throws Exception
{
final XmlContentModel XmlContentModel =
choice
(
0, -1,
element( "A", 1, 1 ),
element( "B", 1, 1 ),
element( "C", 1, 1 )
).create( null );
final String start = "<root><A/><A/><B/><A/><C/><B/></root>";
final String expected = "<root><A/><A/><B/><A/><C/><B/><A/></root>";
test( XmlContentModel, start, "A", expected );
}
@Test
public void test_2B() throws Exception
{
final XmlContentModel XmlContentModel =
choice
(
0, -1,
element( "A", 1, 1 ),
element( "B", 1, 1 ),
element( "C", 1, 1 )
).create( null );
final String start = "<root><A/><A/><B/><A/><C/><B/></root>";
final String expected = "<root><A/><A/><B/><A/><C/><B/><B/></root>";
test( XmlContentModel, start, "B", expected );
}
@Test
public void test_2C() throws Exception
{
final XmlContentModel XmlContentModel =
choice
(
0, -1,
element( "A", 1, 1 ),
element( "B", 1, 1 ),
element( "C", 1, 1 )
).create( null );
final String start = "<root><A/><A/><B/><A/><C/><B/></root>";
final String expected = "<root><A/><A/><B/><A/><C/><B/><C/></root>";
test( XmlContentModel, start, "C", expected );
}
@Test
public void test_3A() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "B", 0, -1 ),
choice
(
0, -1,
element( "C", 1, 1 ),
element( "D", 0, -1 )
),
element( "E", 1, 1 )
).create( null );
final String start =
"<root>\n" +
" <A/>\n" +
" <B/>\n" +
" <B/>\n" +
" <C/>\n" +
" <D/>\n" +
" <D/>\n" +
" <D/>\n" +
" <C/>\n" +
" <E/>\n" +
"</root>";
final String res1 =
"<root>\n" +
" <A/>\n" +
" <B/>\n" +
" <B/>\n" +
" <B/>\n" +
" <C/>\n" +
" <D/>\n" +
" <D/>\n" +
" <D/>\n" +
" <C/>\n" +
" <E/>\n" +
"</root>";
test( XmlContentModel, start, "B", res1 );
}
@Test
public void test_3B() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "B", 0, -1 ),
choice
(
0, -1,
element( "C", 1, 1 ),
element( "D", 0, -1 )
),
element( "E", 1, 1 )
).create( null );
final String start =
"<root>\n" +
" <A/>\n" +
" <B/>\n" +
" <B/>\n" +
" <C/>\n" +
" <D/>\n" +
" <D/>\n" +
" <D/>\n" +
" <C/>\n" +
" <E/>\n" +
"</root>";
final String res2 =
"<root>\n" +
" <A/>\n" +
" <B/>\n" +
" <B/>\n" +
" <C/>\n" +
" <D/>\n" +
" <D/>\n" +
" <D/>\n" +
" <C/>\n" +
" <C/>\n" +
" <E/>\n" +
"</root>";
test( XmlContentModel, start, "C", res2 );
}
@Test
public void test_3C() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "B", 0, -1 ),
choice
(
0, -1,
element( "C", 1, 1 ),
element( "D", 0, -1 )
),
element( "E", 1, 1 )
).create( null );
final String start =
"<root>\n" +
" <A/>\n" +
" <B/>\n" +
" <B/>\n" +
" <C/>\n" +
" <D/>\n" +
" <D/>\n" +
" <D/>\n" +
" <C/>\n" +
" <E/>\n" +
"</root>";
final String res3 =
"<root>\n" +
" <A/>\n" +
" <B/>\n" +
" <B/>\n" +
" <C/>\n" +
" <D/>\n" +
" <D/>\n" +
" <D/>\n" +
" <D/>\n" +
" <C/>\n" +
" <E/>\n" +
"</root>";
// ABBCDDDCE -> ABBCDDDDCE
test( XmlContentModel, start, "D", res3 );
}
@Test
public void test_3D() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "B", 0, -1 ),
choice
(
0, -1,
element( "C", 1, 1 ),
element( "D", 0, -1 )
),
element( "E", 1, 1 )
).create( null );
final String start =
"<root>\n" +
" <A/>\n" +
" <B/>\n" +
" <B/>\n" +
" <C/>\n" +
" <D/>\n" +
" <D/>\n" +
" <D/>\n" +
" <C/>\n" +
" <E/>\n" +
"</root>";
final String res4 =
"<root>\n" +
" <A/>\n" +
" <B/>\n" +
" <B/>\n" +
" <C/>\n" +
" <D/>\n" +
" <D/>\n" +
" <D/>\n" +
" <C/>\n" +
" <E/>\n" +
" <E/>\n" +
"</root>";
test( XmlContentModel, start, "E", res4 );
}
@Test
public void test_4A() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "B", 1, 1 ),
element( "C", 1, 1 ),
sequence
(
1, -1,
element( "D", 1, 1 ),
element( "B", 0, -1 ),
element( "E", 1, 1 )
)
).create( null );
// AC -> ACB
final String start = "<root><A/><C/></root>";
final String expected = "<root><A/><C/><B/></root>";
test( XmlContentModel, start, "B", expected );
}
@Test
public void test_4B() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "B", 1, 1 ),
element( "C", 1, 1 ),
sequence
(
1, -1,
element( "D", 1, 1 ),
element( "B", 0, -1 ),
element( "E", 1, 1 )
)
).create( null );
// ACDBBE -> ABCDBBE
final String start = "<root><A/><C/><D/><B/><B/><E/></root>";
final String expected = "<root><A/><B/><C/><D/><B/><B/><E/></root>";
test( XmlContentModel, start, "B", expected );
}
@Test
public void test_4C() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "B", 1, 1 ),
element( "C", 1, 1 ),
sequence
(
1, -1,
element( "D", 1, 1 ),
element( "B", 0, -1 ),
element( "E", 1, 1 )
)
).create( null );
// ABCDBBE -> ABCDBBBE
final String start = "<root><A/><B/><C/><D/><B/><B/><E/></root>";
final String expected = "<root><A/><B/><C/><D/><B/><B/><B/><E/></root>";
test( XmlContentModel, start, "B", expected );
}
@Test
public void test_5A() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "X", 1, 1 ),
element( "B", 0, -1 ),
choice
(
0, -1,
sequence
(
1, 1,
element( "D", 1, 1 ),
element( "X", 0, 1 ),
element( "C", 1, 1 )
),
sequence
(
1, 1,
element( "X", 0, 1 ),
element( "D", 0, -1 ),
element( "E", 1, 1 )
)
)
).create( null );
// ABDC -> AXBDC
final String start = "<root><A/><B/><D/><C/></root>";
final String expected = "<root><A/><X/><B/><D/><C/></root>";
test( XmlContentModel, start, "X", expected );
}
@Test
public void test_5B() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "X", 1, 1 ),
element( "B", 0, -1 ),
choice
(
0, -1,
sequence
(
1, 1,
element( "D", 1, 1 ),
element( "X", 0, 1 ),
element( "C", 1, 1 )
),
sequence
(
1, 1,
element( "X", 0, 1 ),
element( "D", 0, -1 ),
element( "E", 1, 1 )
)
)
).create( null );
// AXBDC -> AXBDXC
final String start = "<root><A/><X/><B/><D/><C/></root>";
final String expected = "<root><A/><X/><B/><D/><X/><C/></root>";
test( XmlContentModel, start, "X", expected );
}
@Test
public void test_5C() throws Exception
{
final XmlContentModel XmlContentModel =
sequence
(
1, 1,
element( "A", 1, 1 ),
element( "X", 1, 1 ),
element( "B", 0, -1 ),
choice
(
0, -1,
sequence
(
1, 1,
element( "D", 1, 1 ),
element( "X", 0, 1 ),
element( "C", 1, 1 )
),
sequence
(
1, 1,
element( "X", 0, 1 ),
element( "D", 0, -1 ),
element( "E", 1, 1 )
)
)
).create( null );
// AXBDE -> AXBXDE
final String start = "<root><A/><X/><B/><D/><E/></root>";
final String expected = "<root><A/><X/><B/><X/><D/><E/></root>";
test( XmlContentModel, start, "X", expected );
}
private static void test( final XmlContentModel XmlContentModel,
final String initialContent,
final String elementNameToInsert,
final String expectedContent )
throws Exception
{
Element root = parse( initialContent );
NodeList nodeList = root.getChildNodes();
final int position = XmlContentModel.findInsertionPosition( nodeList, new QName( elementNameToInsert ) );
final Element elementToInsert = root.getOwnerDocument().createElementNS( null, elementNameToInsert );
if( position == -1 )
{
root.appendChild( elementToInsert );
}
else
{
final Node nodeAtPosition = nodeList.item( position );
root.insertBefore( elementToInsert, nodeAtPosition );
}
final Element expectedContentRoot = parse( expectedContent );
if( ! equal( nodeList, expectedContentRoot.getChildNodes() ) )
{
final StringBuilder buf = new StringBuilder();
buf.append( "=== actual ===\n" );
buf.append( toString( root.getOwnerDocument() ) );
buf.append( "\n=== expected ===\n" );
buf.append( expectedContent );
assertTrue( buf.toString(), false );
}
}
private static XmlChoiceGroup.Factory choice( final int minOccur,
final int maxOccur,
final XmlContentModel.Factory... list )
{
final XmlChoiceGroup.Factory factory = new XmlChoiceGroup.Factory();
factory.setMinOccur( minOccur );
factory.setMaxOccur( maxOccur );
for( XmlContentModel.Factory child : list )
{
factory.addNestedContent( child );
}
return factory;
}
private static XmlSequenceGroup.Factory sequence( final int minOccur,
final int maxOccur,
final XmlContentModel.Factory... list )
{
final XmlSequenceGroup.Factory factory = new XmlSequenceGroup.Factory();
factory.setMinOccur( minOccur );
factory.setMaxOccur( maxOccur );
for( XmlContentModel.Factory child : list )
{
factory.addNestedContent( child );
}
return factory;
}
private static XmlElementDefinition.Factory element( final String name,
final int minOccur,
final int maxOccur )
{
final XmlElementDefinition.Factory factory = new XmlElementDefinition.Factory();
factory.setName( name );
factory.setMinOccur( minOccur );
factory.setMaxOccur( maxOccur );
return factory;
}
private static Element parse( final String content )
throws Exception
{
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware( true );
final DocumentBuilder docbuilder = factory.newDocumentBuilder();
final Document doc = docbuilder.parse( new InputSource( new StringReader( content ) ) );
return doc.getDocumentElement();
}
private static boolean equal( final NodeList xList,
final NodeList yList )
{
int x = 0;
int y = 0;
int xLen = xList.getLength();
int yLen = yList.getLength();
while( x < xLen && y < yLen )
{
for( int i = x; i < xLen; i++ )
{
if( xList.item( i ).getNodeType() != Node.ELEMENT_NODE )
{
x++;
}
else
{
break;
}
}
for( int i = y; i < yLen; i++ )
{
if( yList.item( i ).getNodeType() != Node.ELEMENT_NODE )
{
y++;
}
else
{
break;
}
}
if( x < xLen && y < yLen )
{
final Node xnode = xList.item( x );
final Node ynode = yList.item( y );
if( xnode.getLocalName().equals( ynode.getLocalName() ) )
{
x++;
y++;
}
else
{
return false;
}
}
}
for( int i = x; i < xLen; i++ )
{
if( xList.item( i ).getNodeType() == Node.ELEMENT_NODE )
{
return false;
}
}
for( int i = y; i < yLen; i++ )
{
if( yList.item( i ).getNodeType() == Node.ELEMENT_NODE )
{
return false;
}
}
return true;
}
private static String toString( final Document doc )
throws Exception
{
final DOMSource source = new DOMSource( doc );
final StringWriter sw = new StringWriter();
final StreamResult result = new StreamResult( sw );
final TransformerFactory factory = TransformerFactory.newInstance();
final Transformer transformer = factory.newTransformer();
transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
transformer.transform( source, result );
return sw.toString();
}
}