Wednesday, June 17, 2015

Adapter pattern for XML passing in Android

For the xml parsing in android environments, usually XMLPullParser is recommended in android docs. This example uses ExpatPullParser.
ExpatPullParser has pre-defined set of interfaces. So what we need is, adapting the interfaces of this class according to our needs. According to wikpedia and this stackoverflow question, adapter pattern is the right candidate for this situation.
This is our xml file.
<Window dynamic="true" delay="10">
 <controller automationId=\"Settings1\" monitoring=\"false\" check=\"existance\">   </controller>
 <controller automationId=\"Settings2\" monitoring=\"true\" check=\"existance\">   </controller>
</Window>
    
So lets first create models and the necessary interfaces.
public interface IBasicModel {
}
import java.util.List;

public interface IWindow extends IBasicModel {
    public boolean isDynamic();
    public String getDelay();
    public void setDynamic(boolean dynamic);
    public void setDelay(String delay);
    public void setControllerList(List controllerList);
}
import java.util.ArrayList;
import java.util.List;

public class Window implements IWindow{

    private boolean dynamic;
    private String delay;
    private List controllerList;

    public Window () {
        controllerList = new ArrayList();
    }

    public boolean isDynamic() {
        return dynamic;
    }

    public String getDelay() {
        return delay;
    }

    public void setDynamic(boolean dynamic) {
        this.dynamic = dynamic;
    }

    public void setDelay(String delay) {
        this.delay = delay;
    }

    public void setControllerList(List controllerList) {
        this.controllerList = controllerList;
    }
}
public interface IController extends IBasicModel{
    public String getAutomationId();

    public boolean isMonitoring();

    public String getCheck();

    public void setAutomationId(String automationId);

    public void setMonitoring(boolean monitoring);

    public void setCheck(String check);
}
public class Controller implements IController{
    private String automationId;
    private boolean monitoring;
    private String check;

    public String getAutomationId() {
        return automationId;
    }

    public boolean isMonitoring() {
        return monitoring;
    }

    public String getCheck() {
        return check;
    }

    public void setAutomationId(String automationId) {
        this.automationId = automationId;
    }

    public void setMonitoring(boolean monitoring) {
        this.monitoring = monitoring;
    }

    public void setCheck(String check) {
        this.check = check;
    }
}
Now what we need is adapting the interfaces of ExpatPullParser according to our requirements.
First we create an abstract class as follows.
public abstract class AbstractXMLAdapter {
    protected T1 parser;
    public abstract IWindow parseXML(T2 xmlResource);
    public abstract IWindow processWindowTag() throws Exception;
    public abstract IController processControllerTag() throws Exception;
}
Here is the concrete implementation of the adapter using ExpatFullParser
public class ExpatPullParserAdapter extends AbstractXMLAdapter<XmlPullParser, String> {

    @Override
    public IWindow parseXML(String xmlResource) {
        try {
            parser = Xml.newPullParser();
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
            parser.setInput(new StringReader(xmlResource));
            parser.nextTag();
            return processWindowTag();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public IWindow processWindowTag() throws Exception {

        List entries = new ArrayList();
        parser.require(XmlPullParser.START_TAG, null, "Window");

        IWindowBuilder windowBuilder = new WindowBuilder();
        windowBuilder.processDelayTag(parser.getAttributeValue(null, "delay"));
        windowBuilder.processDynamicTag(parser.getAttributeValue(null, "dynamic"));

        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
            String name = parser.getName();
            // Starts by looking for the entry tag
            if (name.equals("controller")) {
                entries.add(processControllerTag());
            } else {
                skip(parser);
            }
        }
        windowBuilder.setControllerList(entries);
        return (IWindow)windowBuilder.getInstace();
    }

    private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            throw new IllegalStateException();
        }
        int depth = 1;
        while (depth != 0) {
            switch (parser.next()) {
                case XmlPullParser.END_TAG:
                    depth--;
                    break;
                case XmlPullParser.START_TAG:
                    depth++;
                    break;
            }
        }
    }

    @Override
    public IController processControllerTag() throws Exception {
        IControllerBuilder builder = new ControllerBuilder();
        builder.processAutomationId(parser.getAttributeValue(null, "automationId"));
        builder.processMonitoring(parser.getAttributeValue(null, "monitoring"));
        builder.processCheck(parser.getAttributeValue(null, "check"));

        while (parser.next() != XmlPullParser.END_TAG) {
            if (parser.getEventType() != XmlPullParser.START_TAG) {
                continue;
            }
        }

        return (IController)builder.getInstace();
    }
}
This is the implementation of adapter pattern. Now next question is, generating the models. For that purpose, I have used builder pattern

Use of Builder pattern in model class generation
In order to decouple the model class generation from xml parsing, I have used builder pattern. Here what we do is, generating a complex class structure.So in this situation, builder is useful than something like abstract factory. I will show you how Controller models were generated using the builder pattern.
public interface IBuilder {
    public IBasicModel getInstace();
}
public interface IControllerBuilder extends IBuilder {
    public void processAutomationId(String automationId);
    public void processMonitoring(String monitoring);
    public void processCheck(String check);
}

public class ControllerBuilder implements IControllerBuilder{
    private IController controller;

    public ControllerBuilder() {
        controller = new Controller();
    }

    @Override
    public IController getInstace() {
        return controller;
    }

    @Override
    public void processAutomationId(String automationId) {
        controller.setAutomationId(automationId);
    }

    @Override
    public void processMonitoring(String monitoring) {
        controller.setMonitoring(Boolean.parseBoolean(monitoring));
    }

    @Override
    public void processCheck(String check) {
        controller.setCheck(check);
    }
}
Complete example is now available in the github. For any query, feel free to contact me via my linkedin profile.