/** * Copyright 2010 JBoss Inc * * 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.drools.runtime.help.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.drools.base.ClassObjectType; import org.drools.base.DroolsQuery; import org.drools.command.Command; import org.drools.command.CommandFactory; import org.drools.command.Setter; import org.drools.command.runtime.BatchExecutionCommandImpl; import org.drools.command.runtime.GetGlobalCommand; import org.drools.command.runtime.SetGlobalCommand; import org.drools.command.runtime.process.AbortWorkItemCommand; import org.drools.command.runtime.process.CompleteWorkItemCommand; import org.drools.command.runtime.process.SignalEventCommand; import org.drools.command.runtime.process.StartProcessCommand; import org.drools.command.runtime.rule.FireAllRulesCommand; import org.drools.command.runtime.rule.GetObjectCommand; import org.drools.command.runtime.rule.GetObjectsCommand; import org.drools.command.runtime.rule.InsertElementsCommand; import org.drools.command.runtime.rule.InsertObjectCommand; import org.drools.command.runtime.rule.ModifyCommand; import org.drools.command.runtime.rule.QueryCommand; import org.drools.command.runtime.rule.RetractCommand; import org.drools.common.DefaultFactHandle; import org.drools.rule.Declaration; import org.drools.runtime.ExecutionResults; import org.drools.runtime.impl.ExecutionResultImpl; import org.drools.runtime.rule.FactHandle; import org.drools.runtime.rule.QueryResults; import org.drools.runtime.rule.QueryResultsRow; import org.drools.runtime.rule.impl.FlatQueryResults; import org.drools.runtime.rule.impl.NativeQueryResults; import org.drools.spi.ObjectType; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter; import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; import com.thoughtworks.xstream.mapper.Mapper; public class XStreamXML { public static XStream newXStreamMarshaller(XStream xstream) { XStreamHelper.setAliases( xstream ); xstream.processAnnotations( BatchExecutionCommandImpl.class ); xstream.addImplicitCollection( BatchExecutionCommandImpl.class, "commands" ); xstream.registerConverter( new InsertConverter( xstream ) ); xstream.registerConverter( new RetractConverter( xstream ) ); xstream.registerConverter( new ModifyConverter( xstream ) ); xstream.registerConverter( new GetObjectConverter( xstream ) ); xstream.registerConverter( new InsertElementsConverter( xstream ) ); xstream.registerConverter( new FireAllRulesConverter( xstream ) ); xstream.registerConverter( new StartProcessConvert( xstream ) ); xstream.registerConverter( new SignalEventConverter( xstream ) ); xstream.registerConverter( new CompleteWorkItemConverter( xstream ) ); xstream.registerConverter( new AbortWorkItemConverter( xstream ) ); xstream.registerConverter( new QueryConverter( xstream ) ); xstream.registerConverter( new SetGlobalConverter( xstream ) ); xstream.registerConverter( new GetGlobalConverter( xstream ) ); xstream.registerConverter( new GetObjectsConverter( xstream ) ); xstream.registerConverter( new BatchExecutionResultConverter( xstream ) ); xstream.registerConverter( new QueryResultsConverter( xstream ) ); xstream.registerConverter( new FactHandleConverter( xstream ) ); return xstream; } public static class InsertConverter extends AbstractCollectionConverter implements Converter { public InsertConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { InsertObjectCommand cmd = (InsertObjectCommand) object; if ( cmd.getOutIdentifier() != null ) { writer.addAttribute( "out-identifier", cmd.getOutIdentifier() ); writer.addAttribute( "return-object", Boolean.toString( cmd.isReturnObject() ) ); writer.addAttribute( "entry-point", cmd.getEntryPoint() ); } writeItem( cmd.getObject(), context, writer ); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String identifierOut = reader.getAttribute( "out-identifier" ); String returnObject = reader.getAttribute( "return-object" ); String entryPoint = reader.getAttribute( "entry-point" ); reader.moveDown(); Object object = readItem( reader, context, null ); reader.moveUp(); InsertObjectCommand cmd = new InsertObjectCommand( object ); if ( identifierOut != null ) { cmd.setOutIdentifier( identifierOut ); if ( returnObject != null ) { cmd.setReturnObject( Boolean.parseBoolean( returnObject ) ); } } if ( entryPoint != null ) { cmd.setEntryPoint( entryPoint ); } return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( InsertObjectCommand.class ); } } public static class FactHandleConverter implements Converter { private Mapper mapper; public FactHandleConverter(XStream xstream) { this.mapper = mapper; } public boolean canConvert(Class aClass) { return FactHandle.class.isAssignableFrom( aClass ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext marshallingContext) { FactHandle fh = (FactHandle) object; //writer.startNode("fact-handle"); writer.addAttribute( "external-form", fh.toExternalForm() ); //writer.endNode(); } public Object unmarshal(HierarchicalStreamReader hierarchicalStreamReader, UnmarshallingContext unmarshallingContext) { throw new UnsupportedOperationException( "Unable to unmarshal fact handles." ); } } public static class ModifyConverter implements Converter { public ModifyConverter(XStream xstream) { } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { ModifyCommand cmd = (ModifyCommand) object; writer.addAttribute( "fact-handle", cmd.getFactHandle().toExternalForm() ); for ( Setter setter : cmd.getSetters() ) { writer.startNode( "set" ); writer.addAttribute( "accessor", setter.getAccessor() ); writer.addAttribute( "value", setter.getValue() ); writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { FactHandle factHandle = new DefaultFactHandle( reader.getAttribute( "fact-handle" ) ); List<Setter> setters = new ArrayList(); while ( reader.hasMoreChildren() ) { reader.moveDown(); Setter setter = CommandFactory.newSetter( reader.getAttribute( "accessor" ), reader.getAttribute( "value" ) ); setters.add( setter ); reader.moveUp(); } Command cmd = CommandFactory.newModify( factHandle, setters ); return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( ModifyCommand.class ); } } public static class RetractConverter extends AbstractCollectionConverter implements Converter { public RetractConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { RetractCommand cmd = (RetractCommand) object; writer.addAttribute( "fact-handle", cmd.getFactHandle().toExternalForm() ); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { FactHandle factHandle = new DefaultFactHandle( reader.getAttribute( "fact-handle" ) ); Command cmd = CommandFactory.newRetract( factHandle ); return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( RetractCommand.class ); } } public static class InsertElementsConverter extends AbstractCollectionConverter implements Converter { public InsertElementsConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { InsertElementsCommand cmd = (InsertElementsCommand) object; if ( cmd.getOutIdentifier() != null ) { writer.addAttribute( "out-identifier", cmd.getOutIdentifier() ); writer.addAttribute( "return-objects", Boolean.toString( cmd.isReturnObject() ) ); writer.addAttribute( "entry-point", cmd.getEntryPoint() ); } for ( Object element : cmd.getObjects() ) { writeItem( element, context, writer ); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String identifierOut = reader.getAttribute( "out-identifier" ); String returnObject = reader.getAttribute( "return-objects" ); String entryPoint = reader.getAttribute( "entry-point" ); List objects = new ArrayList(); while ( reader.hasMoreChildren() ) { reader.moveDown(); Object object = readItem( reader, context, null ); reader.moveUp(); objects.add( object ); } InsertElementsCommand cmd = new InsertElementsCommand( objects ); if ( identifierOut != null ) { cmd.setOutIdentifier( identifierOut ); if ( returnObject != null ) { cmd.setReturnObject( Boolean.parseBoolean( returnObject ) ); } } if ( entryPoint != null ) { cmd.setEntryPoint( entryPoint ); } return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( InsertElementsCommand.class ); } } public static class SetGlobalConverter extends AbstractCollectionConverter implements Converter { public SetGlobalConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { SetGlobalCommand cmd = (SetGlobalCommand) object; writer.addAttribute( "identifier", cmd.getIdentifier() ); if ( cmd.getOutIdentifier() != null ) { writer.addAttribute( "out-identifier", cmd.getOutIdentifier() ); } else if ( cmd.isOut() ) { writer.addAttribute( "out", Boolean.toString( cmd.isOut() ) ); } writeItem( cmd.getObject(), context, writer ); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String identifier = reader.getAttribute( "identifier" ); String out = reader.getAttribute( "out" ); String identifierOut = reader.getAttribute( "out-identifier" ); reader.moveDown(); Object object = readItem( reader, context, null ); reader.moveUp(); SetGlobalCommand cmd = new SetGlobalCommand( identifier, object ); if ( identifierOut != null ) { cmd.setOutIdentifier( identifierOut ); } else if ( out != null ) { cmd.setOut( Boolean.parseBoolean( out ) ); } return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( SetGlobalCommand.class ); } } public static class GetObjectConverter extends AbstractCollectionConverter implements Converter { public GetObjectConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { GetObjectCommand cmd = (GetObjectCommand) object; writer.addAttribute( "fact-handle", cmd.getFactHandle().toExternalForm() ); if ( cmd.getOutIdentifier() != null ) { writer.addAttribute( "out-identifier", cmd.getOutIdentifier() ); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { FactHandle factHandle = new DefaultFactHandle( reader.getAttribute( "fact-handle" ) ); String identifierOut = reader.getAttribute( "out-identifier" ); GetObjectCommand cmd = new GetObjectCommand( factHandle ); if ( identifierOut != null ) { cmd.setOutIdentifier( identifierOut ); } return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( GetObjectCommand.class ); } } public static class GetGlobalConverter extends AbstractCollectionConverter implements Converter { public GetGlobalConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { GetGlobalCommand cmd = (GetGlobalCommand) object; writer.addAttribute( "identifier", cmd.getIdentifier() ); if ( cmd.getOutIdentifier() != null ) { writer.addAttribute( "out-identifier", cmd.getOutIdentifier() ); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String identifier = reader.getAttribute( "identifier" ); String identifierOut = reader.getAttribute( "out-identifier" ); GetGlobalCommand cmd = new GetGlobalCommand( identifier ); if ( identifierOut != null ) { cmd.setOutIdentifier( identifierOut ); } return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( GetGlobalCommand.class ); } } public static class GetObjectsConverter extends AbstractCollectionConverter implements Converter { public GetObjectsConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { GetObjectsCommand cmd = (GetObjectsCommand) object; if ( cmd.getOutIdentifier() != null ) { writer.addAttribute( "out-identifier", cmd.getOutIdentifier() ); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String identifierOut = reader.getAttribute( "out-identifier" ); GetObjectsCommand cmd = new GetObjectsCommand(); if ( identifierOut != null ) { cmd.setOutIdentifier( identifierOut ); } return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( GetObjectsCommand.class ); } } public static class FireAllRulesConverter extends AbstractCollectionConverter implements Converter { public FireAllRulesConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { FireAllRulesCommand cmd = (FireAllRulesCommand) object; if ( cmd.getMax() != -1 ) { writer.addAttribute( "max", Integer.toString( cmd.getMax() ) ); } if ( cmd.getOutIdentifier() != null) { writer.addAttribute( "out-identifier", cmd.getOutIdentifier()); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String max = reader.getAttribute( "max" ); String outIdentifier = reader.getAttribute( "out-identifier" ); FireAllRulesCommand cmd = null; if ( max != null ) { cmd = new FireAllRulesCommand( Integer.parseInt( max ) ); } else { cmd = new FireAllRulesCommand(); } if ( outIdentifier != null ) { cmd.setOutIdentifier( outIdentifier ); } return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( FireAllRulesCommand.class ); } } public static class QueryConverter extends AbstractCollectionConverter implements Converter { public QueryConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { QueryCommand cmd = (QueryCommand) object; writer.addAttribute( "out-identifier", cmd.getOutIdentifier() ); writer.addAttribute( "name", cmd.getName() ); if ( cmd.getArguments() != null ) { for ( Object arg : cmd.getArguments() ) { writeItem( arg, context, writer ); } } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { List<String> outs = new ArrayList<String>(); // Query cmd = null; String outIdentifier = reader.getAttribute( "out-identifier" ); String name = reader.getAttribute( "name" ); List<Object> args = new ArrayList<Object>(); while ( reader.hasMoreChildren() ) { reader.moveDown(); Object arg = readItem( reader, context, null ); args.add( arg ); reader.moveUp(); } QueryCommand cmd = new QueryCommand( outIdentifier, name, args.toArray( new Object[args.size()] ) ); return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( QueryCommand.class ); } } public static class StartProcessConvert extends AbstractCollectionConverter implements Converter { public StartProcessConvert(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { StartProcessCommand cmd = (StartProcessCommand) object; writer.addAttribute( "processId", cmd.getProcessId() ); writer.addAttribute( "out-identifier", cmd.getOutIdentifier() ); for ( Entry<String, Object> entry : cmd.getParameters().entrySet() ) { writer.startNode( "parameter" ); writer.addAttribute( "identifier", entry.getKey() ); writeItem( entry.getValue(), context, writer ); writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String processId = reader.getAttribute( "processId" ); String outIdentifier = reader.getAttribute( "out-identifier" ); HashMap<String, Object> params = new HashMap<String, Object>(); while ( reader.hasMoreChildren() ) { reader.moveDown(); String identifier = reader.getAttribute( "identifier" ); reader.moveDown(); Object value = readItem( reader, context, null ); reader.moveUp(); params.put( identifier, value ); reader.moveUp(); } StartProcessCommand cmd = new StartProcessCommand(); cmd.setProcessId( processId ); cmd.setParameters( params ); cmd.setOutIdentifier( outIdentifier ); return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( StartProcessCommand.class ); } } public static class SignalEventConverter extends AbstractCollectionConverter implements Converter { public SignalEventConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { SignalEventCommand cmd = (SignalEventCommand) object; long processInstanceId = cmd.getProcessInstanceId(); String eventType = cmd.getEventType(); Object event = cmd.getEvent(); if ( processInstanceId != -1 ) { writer.addAttribute( "process-instance-id", Long.toString( processInstanceId ) ); } writer.addAttribute( "event-type", eventType ); writeItem( event, context, writer ); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String processInstanceId = reader.getAttribute( "process-instance-id" ); String eventType = reader.getAttribute( "event-type" ); reader.moveDown(); Object event = readItem( reader, context, null ); reader.moveUp(); Command cmd; if ( processInstanceId != null ) { cmd = CommandFactory.newSignalEvent( Long.parseLong( processInstanceId ), eventType, event ); } else { cmd = CommandFactory.newSignalEvent( eventType, event ); } return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( SignalEventCommand.class ); } } public static class CompleteWorkItemConverter extends AbstractCollectionConverter implements Converter { public CompleteWorkItemConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { CompleteWorkItemCommand cmd = (CompleteWorkItemCommand) object; writer.addAttribute( "id", Long.toString( cmd.getWorkItemId() ) ); for ( Entry<String, Object> entry : cmd.getResults().entrySet() ) { writer.startNode( "result" ); writer.addAttribute( "identifier", entry.getKey() ); writeItem( entry.getValue(), context, writer ); writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String id = reader.getAttribute( "id" ); Map<String, Object> results = new HashMap<String, Object>(); while ( reader.hasMoreChildren() ) { reader.moveDown(); String identifier = reader.getAttribute( "identifier" ); reader.moveDown(); Object value = readItem( reader, context, null ); reader.moveUp(); results.put( identifier, value ); reader.moveUp(); } Command cmd = CommandFactory.newCompleteWorkItem( Long.parseLong( id ), results ); return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( CompleteWorkItemCommand.class ); } } public static class AbortWorkItemConverter extends AbstractCollectionConverter implements Converter { public AbortWorkItemConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { AbortWorkItemCommand cmd = (AbortWorkItemCommand) object; writer.addAttribute( "id", Long.toString( cmd.getWorkItemId() ) ); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String id = reader.getAttribute( "id" ); Command cmd = CommandFactory.newAbortWorkItem( Long.parseLong( id ) ); return cmd; } public boolean canConvert(Class clazz) { return clazz.equals( AbortWorkItemCommand.class ); } } public static class BatchExecutionResultConverter extends AbstractCollectionConverter implements Converter { public BatchExecutionResultConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { ExecutionResults result = (ExecutionResults) object; for ( String identifier : result.getIdentifiers() ) { writer.startNode( "result" ); writer.addAttribute( "identifier", identifier ); Object value = result.getValue( identifier ); if ( value instanceof org.drools.runtime.rule.QueryResults ) { String name = mapper().serializedClass(FlatQueryResults.class); ExtendedHierarchicalStreamWriterHelper.startNode(writer, name, FlatQueryResults.class); context.convertAnother(value); writer.endNode(); } else { writeItem( value, context, writer ); } writer.endNode(); } for ( String identifier : ((ExecutionResultImpl) result).getFactHandles().keySet() ) { Object handle = result.getFactHandle( identifier ); if ( handle instanceof FactHandle ) { writer.startNode( "fact-handle" ); writer.addAttribute( "identifier", identifier ); writer.addAttribute( "external-form", ((FactHandle) handle).toExternalForm() ); writer.endNode(); } else if ( handle instanceof Collection ) { writer.startNode( "fact-handles" ); writer.addAttribute( "identifier", identifier ); for ( FactHandle factHandle : (Collection<FactHandle>) handle ) { writer.startNode( "fact-handle" ); writer.addAttribute( "external-form", ((FactHandle) factHandle).toExternalForm() ); writer.endNode(); } writer.endNode(); } } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { ExecutionResultImpl result = new ExecutionResultImpl(); Map results = result.getResults(); Map facts = result.getFactHandles(); while ( reader.hasMoreChildren() ) { reader.moveDown(); if ( reader.getNodeName().equals( "result" ) ) { String identifier = reader.getAttribute( "identifier" ); reader.moveDown(); Object value = readItem( reader, context, null ); results.put( identifier, value ); reader.moveUp(); reader.moveUp(); } else if ( reader.getNodeName().equals( "fact-handle" ) ) { String identifier = reader.getAttribute( "identifier" ); facts.put( identifier, new DefaultFactHandle( reader.getAttribute( "external-form" ) ) ); } else if ( reader.getNodeName().equals( "fact-handles" ) ) { String identifier = reader.getAttribute( "identifier" ); List<FactHandle> list = new ArrayList(); while ( reader.hasMoreChildren() ) { reader.moveDown(); list.add( new DefaultFactHandle( reader.getAttribute( "external-form" ) ) ); reader.moveUp(); } facts.put( identifier, list ); } else { throw new IllegalArgumentException( "Element '" + reader.getNodeName() + "' is not supported here" ); } } return result; } public boolean canConvert(Class clazz) { return ExecutionResults.class.isAssignableFrom( clazz ); } } public static class QueryResultsConverter extends AbstractCollectionConverter implements Converter { public QueryResultsConverter(XStream xstream) { super( xstream.getMapper() ); } public void marshal(Object object, HierarchicalStreamWriter writer, MarshallingContext context) { QueryResults results = (QueryResults) object; // write out identifiers List<String> originalIds = Arrays.asList( results.getIdentifiers() ); List<String> actualIds = new ArrayList(); if ( results instanceof NativeQueryResults ) { for ( String identifier : originalIds ) { // we don't want to marshall the query parameters Declaration declr = ((NativeQueryResults) results).getDeclarations().get( identifier ); ObjectType objectType = declr.getPattern().getObjectType(); if ( objectType instanceof ClassObjectType ) { if ( ((ClassObjectType) objectType).getClassType() == DroolsQuery.class ) { continue; } } actualIds.add( identifier ); } } String[] identifiers = actualIds.toArray( new String[actualIds.size()] ); writer.startNode( "identifiers" ); for ( int i = 0; i < identifiers.length; i++ ) { writer.startNode( "identifier" ); writer.setValue( identifiers[i] ); writer.endNode(); } writer.endNode(); for ( QueryResultsRow result : results ) { writer.startNode( "row" ); for ( int i = 0; i < identifiers.length; i++ ) { Object value = result.get( identifiers[i] ); FactHandle factHandle = result.getFactHandle( identifiers[i] ); writeItem( value, context, writer ); writer.startNode( "fact-handle" ); writer.addAttribute( "external-form", ((FactHandle) factHandle).toExternalForm() ); writer.endNode(); } writer.endNode(); } } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { reader.moveDown(); List<String> list = new ArrayList<String>(); while ( reader.hasMoreChildren() ) { reader.moveDown(); list.add( reader.getValue() ); reader.moveUp(); } reader.moveUp(); HashMap<String, Integer> identifiers = new HashMap<String, Integer>(); for ( int i = 0; i < list.size(); i++ ) { identifiers.put( list.get( i ), i ); } ArrayList<ArrayList<Object>> results = new ArrayList(); ArrayList<ArrayList<FactHandle>> resultHandles = new ArrayList(); while ( reader.hasMoreChildren() ) { reader.moveDown(); ArrayList objects = new ArrayList(); ArrayList<FactHandle> handles = new ArrayList(); while ( reader.hasMoreChildren() ) { reader.moveDown(); Object object = readItem( reader, context, null ); reader.moveUp(); reader.moveDown(); FactHandle handle = new DefaultFactHandle( reader.getAttribute( "external-form" ) ); reader.moveUp(); objects.add( object ); handles.add( handle ); } results.add( objects ); resultHandles.add( handles ); reader.moveUp(); } return new FlatQueryResults( identifiers, results, resultHandles ); } public boolean canConvert(Class clazz) { return QueryResults.class.isAssignableFrom( clazz ); } } }