Code Monkey home page Code Monkey logo

kotlin_xml_parser's Introduction

XML Parser

This project has 2 main modules:

  1. xmlparser.core - Standalone library that creates and edits xml documents with packages/classes/methods well documented.
  2. xmlparser.editor - A graphical user interface that helps to edit a xml document. It uses the xmlparse.core library and its highly extendable using plugins.

xmlparser.core

Basic usage

First we need to create a xml context, we can do that with the following code:

val xmlContext: XmlContext = XmlContext()

A xml file must have a root/principal node, we can create a xml element and set it as the root element. We can do this by:

val rootElement: XmlElement = XmlElement(name = "Root")
xmlContext.rootXmlElement = rootElement

To dump a xml context we can do the following:

xmlContext.dump() // or toString()

Xml Elements and Attributes

A xml element is a something commonly known as a xml tag, this is a xml element:

<element></element>

A xml element can have a value:

<element>Value Xml Element</element>

And multiple attributes:

<element attribute1="valueAttribute2" attribute2="valueAttribute2"></element>

A xml element can also have a child or multiple children:

<element>
    <child></child>
</element>

To create a xml element we need to instantiate the XmlElement class:

val xmlElement: XmlElement = XmlElement("name", "value")

We can add an attribute to a xmlElement like this:

val xmlAttribute: XmlAttribute = XmlAttribute("attributeName", "attributeValue")
xmlElement.addAttribute(xmlAttribute)

We can a child to a xml element this way:

val xmlElementChild: XmlElement = XmlElement("child", "valueChild")
xmlElement.addChild(xmlElementChild) // or xmlElement.addChild("child", "valueChild")

Visiting and observing

A xml context is visitable and observable. It contains 2 visitors but more can be created using IVisitor interface:

  1. Find Visitor
  2. Filter Visitor

It also contains many notification types that can be used in our observers to handle different types of situations.

Standard Containers Support

xmlparser.core supports:

  1. Arrays
  2. Classes that implement iterable(set and list)
  3. Maps

Example:

val list = listOf(1, 2, 3)
val element = XmlElement(list, name = "iterable")
xmlContext.rootXmlElement = element

This way, the context will be:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<iterable>
    <item>1</item>
    <item>2</item>
    <item>3</item>
</iterable>

Serializing classes - Reflection

In xmlparser.core it's very easy to serialize custom classes. But first, let's see the available annotations that we can use to better customize the behaviour of the serialization.

Name Usage
XmlElementName Name of the element
XmlElementContent Content of the element
XmlElementIgnore This element will be ignored
XmlElementAttribute Attribute of the element

Example of usage of the annotations when serializing a custom class.

private data class Entity(private val id: Int, val name: String)

private data class Point(val x: Int, val y: Int)

@XmlElementName("ComplexEntity")
private class Complex {
    @XmlElementContent
    val data: String = "Data Example"
    @XmlElementIgnore
    val ignore: String = "Element to ignore"
    @XmlElementAttribute
    val attribute1: String = "Attribute content"
    @XmlElementAttribute
    @XmlElementName("SpecialAttribute")
    val attribute2: String = "Attribute content"

    private val entity: Entity = Entity(1, "1")
    private val point: Point = Point(1,1)
    private val id: Int = 1
    @XmlElementName("maps")
    val map: Map<Int, Point> = mapOf(Pair(0, Point(0, 0)), Pair(1, Point(1, 1)))
}

Serialization:

val complex = Complex()
xmlContext.rootXmlElement = XmlElement(complex)

Result:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ComplexEntity attribute1="Attribute content" SpecialAttribute="Attribute content">Data Example
    <entity>
        <id>1</id>
        <name>1</name>
    </entity>
    <id>1</id>
    <maps>
        <item>
            <key>0</key>
            <value>
                <x>0</x>
                <y>0</y>
            </value>
        </item>
        <item>
            <key>1</key>
            <value>
                <x>1</x>
                <y>1</y>
            </value>
        </item>
    </maps>
    <point>
        <x>1</x>
        <y>1</y>
    </point>
</ComplexEntity>

xmlparser.editor

How to write plugins

xmlparser.editor is highly extensible. We can customize and add new functionalities to the core editor. We have 2 interfaces we can implement to add new functionalities to the application.

Name Definition
IComponent Represents a UI component
IMenuItem Represents a menu item
IAction Represents a Command/Action that can be implemented and used in plugins

IComponent

This interface looks like this:

interface IComponent<T1 : AbstractContextView<T1>, T2 : Component> {
    /**
     * Condition to accept.
     */
    fun accept(view: T1): Boolean = true

    /**
     * Gets the component.
     */
    fun component(view: T1): T2?
}

The accept function is used to know if the component should be applied in a context. The function component will return the component to be drawn on the editor.

class DateAttributeComponent: IComponent<AttributeView, JPanel>
{
    override fun accept(view: AttributeView): Boolean {
        return view.xmlAttribute.name == "Date" &&
                view.xmlElement.name == "Event"
    }
    
    override fun component(view: AttributeView): JPanel {
        val panel = JPanel()
        panel.layout = GridLayout(0,2)
        val label = JLabel(view.xmlAttribute.name)
        label.horizontalAlignment = SwingConstants.RIGHT
        panel.add(label)

        val date = SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy").parse(view.xmlAttribute.value)
        val jSpinner = JSpinner(SpinnerDateModel(date,
            null, null, Calendar.MINUTE))
        jSpinner.addChangeListener {
            val value = jSpinner.value.toString()
            ActionStack.doAction(
                EditAttributeValueAction(
                    view.xmlAttribute,
                    value
                )
            )
        }
        panel.add(jSpinner)
        return panel
    }

}

In this example, this component will be used in an attribute view if the name of the attribute is "Date" and if the xml element of the attribute is "Event". It will return a JPanel with special field made to edit a date. With this plugin and others we could make something like this: img.png

In examples.plugins we have some examples that further explain the usage.

IMenuItem

This interface looks like this:

interface IMenuItem<T1 : AbstractContextView<T1>> {
    /**
     * Condition to accept.
     */
    fun accept(view: T1): Boolean = true
    /**
     * Gets the menu item.
     */
    fun menuItem(view: T1): JMenuItem
}

The accept function is used to know if the component should be applied in a context. The function menuItem will return the component to be available on the editor.

class AddPointMenuItem : IMenuItem<ElementView>
{

    override fun accept(view: ElementView): Boolean = view.xmlElement.name != "Point"

    override fun menuItem(view: ElementView): JMenuItem {
        val jMenuItem = JMenuItem("Add Point")
        jMenuItem.addActionListener {
            val xField = JTextField(5)
            val yField = JTextField(5)

            val jPanel = JPanel()

            jPanel.add(JLabel("x:"))
            jPanel.add(xField)
            jPanel.add(Box.createHorizontalStrut(15))

            jPanel.add(JLabel("y:"))
            jPanel.add(yField)
            val result = JOptionPane.showConfirmDialog(
                null, jPanel,
                "Please Enter X and Y Values", JOptionPane.OK_CANCEL_OPTION
            )
            if (result == JOptionPane.OK_OPTION) {
                if(xField.text == "")
                {
                    JOptionPane.showMessageDialog(view, "Invalid x value")
                    return@addActionListener
                }
                if(yField.text == "")
                {
                    JOptionPane.showMessageDialog(view, "Invalid y value")
                    return@addActionListener
                }

                try
                {
                    Integer.parseInt(xField.text)
                }
                catch (exception: Exception)
                {
                    JOptionPane.showMessageDialog(view, "Invalid x value")
                    return@addActionListener
                }
                try
                {
                    Integer.parseInt(yField.text)
                }
                catch (exception: Exception)
                {
                    JOptionPane.showMessageDialog(view, "Invalid y value")
                    return@addActionListener
                }
                val xmlElement = XmlElement("Point")
                xmlElement.addChild("x", xField.text)
                xmlElement.addChild("y", yField.text)
                ActionStack.doAction(AddChildAction(view.xmlElement, xmlElement))
            }
        }
        return jMenuItem
    }

}

This class will create an option to create special xml element child called "Point" if the father is not called "Point".

In examples.plugins we have some examples that further explain the usage.

IAction

This interface looks like this:

interface IAction {
    /**
     * Action name.
     */
    val name: String

    /**
     * Executes action.
     */
    fun execute()

    /**
     * Undo the action
     */
    fun undo()
}

Can be used to create a custom action to use in plugins.

di.properties

Name Description
MainController.context Injects the context
MainController.elementViewPluginMenuItems Injects Menu items in the element view
MainController.attributeViewPluginMenuItems Injects Menu items in the attribute view
MainController.elementValueViewPluginComponents Injects a component in the element view
MainController.attributeViewPluginComponents Injects a component in the attribute view

kotlin_xml_parser's People

Contributors

tariqyoussef avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.