Lecture 5 XML

XML Examples

Example 1

public class Main {
    public static void main(String[] args) throws Exception {
        // example of how to load and query an XML document
        LoadAndQueryXML();

        // example of how to extract a selection of an existing XML document and add it to a
        // new XML document. the new XML document is then saved.
        CreateAndSaveXML();
    }

    private static void LoadAndQueryXML() throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
        // create DocumentBuilder to parse XML document
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // load document and parse to create tree
        File fin = new File("items.xml");
        Document doc = builder.parse(fin);

        // create xpath object that will allow document to be queried
        XPathFactory xpFactory = XPathFactory.newInstance();
        XPath path = xpFactory.newXPath();

        ///////////////////////////////////////////////////////////////////////////////////////////////////
        // examples of different queries
        ///////////////////////////////////////////////////////////////////////////////////////////////////

        // query document for the 1st item's quantity
        String result = path.evaluate("/items/item[1]/quantity", doc);
        System.out.println("quantity of product[1] = " + result);

        // query document for the currency of the 2nd item's product price
        result = path.evaluate("/items/item[2]/product/price/@currency", doc);
        System.out.println("currency of product[2] = " + result);

        // query document for the product description of any item of which there are 8
        result = path.evaluate("/items/item[quantity='8']/product/description", doc);
        System.out.println("product having quantity of 8 = " + result);

        // query document for the quantity of any item that has a product description of '4-port Mini Hub'
        result = path.evaluate("/items/item[product/description='4-port Mini Hub']/quantity", doc);
        System.out.println("quantity of '4-port Mini Hub' = " + result);

        // how many products are there? note // = any node with name product
        result = path.evaluate("count(//product)", doc);
        System.out.println("count of products = " + result);

        // how many products are there that have prices specified in ZAR amd the price is less than 30 ZAR?
        result = path.evaluate("count(//product[price/@currency='ZAR' and price < 30])", doc);
        System.out.println("count of products = " + result);
    }

    private static void CreateAndSaveXML() throws Exception {
        // create DocumentBuilder to parse XML document
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // load document and parse to create tree
        File fin = new File("items.xml");
        Document doc = builder.parse(fin);

        // create xpath object that will allow document to be queried
        XPathFactory xpFactory = XPathFactory.newInstance();
        XPath path = xpFactory.newXPath();

        // query the document and save out nodes that match a specified criteria
        // create document
        Document outputDoc = createDoc();
        // create root element and attach to document
        Element results = outputDoc.createElement("results");
        outputDoc.appendChild(results);

        //XPathExpression expr = path.compile("//description/text()");
        //XPathExpression expr = path.compile("//item/*/description/text()");
        //XPathExpression expr = path.compile("//item");
        XPathExpression expr = path.compile("//item[quantity>=8 and product/price/@currency='ZAR']");

        // NODESET means more than one node will be returned by the query
        NodeList resultSet = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
        System.out.println("# nodes returned by query = " + resultSet.getLength());

        // unfortunately, the foreach notation does not work for the result set :(
        // i.e. for(Node node : resultSet)
        for (int i = 0; i < resultSet.getLength(); i++) {
            // attach item to root (if you want to use a node from one tree in another, you need to clone it first
            // as a node cannot belong to more than one tree at a time)
            Node node = resultSet.item(i).cloneNode(true);
            // associate the node with the output document
            outputDoc.adoptNode(node);
            // now allowed to attach the nodes together
            results.appendChild(node);
        }

        // get old root
        saveDoc(outputDoc, "items-output.xml");
    }

    /**
     * Create and return an empty document.
     * @return The document that was created.
     * @throws Exception If something went wrong.
     */
    public static Document createDoc() throws Exception {
        // create DocumentBuilder to parse XML document
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // create empty document
        Document doc = builder.newDocument();

        return doc;
    }

    /**
     * <p>Save the document to a file with the given filename.</p>
     * <p>You will be given the code for this method in a test, so you do not need to memorise it.</p>
     * @param doc The document to save.
     * @param filename The filename of the file to which the document will be saved.
     * @throws Exception If something goes wrong, e.g. invalid filename, not enough space, etc.
     */
    public static void saveDoc(Document doc, String filename) throws Exception {
        // obtain serializer
        DOMImplementation impl = doc.getImplementation();
        DOMImplementationLS implLS = (DOMImplementationLS) impl.getFeature("LS", "3.0");
        LSSerializer ser = implLS.createLSSerializer();
        ser.getDomConfig().setParameter("format-pretty-print", true);

        // create file to save too
        FileOutputStream fout = new FileOutputStream(filename);

        // set encoding options
        LSOutput lsOutput = implLS.createLSOutput();
        lsOutput.setEncoding("UTF-8");

        // tell to save xml output to file
        lsOutput.setByteStream(fout);

        // FINALLY write output
        ser.write(doc, lsOutput);

        // close file
        fout.close();
    }
}

Example 2

public class Main {
    /**
     * A method that displays a specific element (and its children) indented on the console.
     *
     * @param element The element to display.
     * @param depth   The current indentation. Each child will be indented one level more than the parent.
     */
    public static void display(Element element, int depth) {
        // create a string of 'depth' tabs
        String indent = "";
        for (int i = 0; i < depth; i++)
            indent = indent.concat("\t");

        // get the name of the node
        String name = element.getNodeName();

        // display the beginning of the node
        System.out.printf("%sBEGIN(%s)\n", indent, name);

        NodeList nodes = element.getChildNodes();
        // for each node, display it (and children if any)
        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i);

            // is it a node containing child nodes?
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                // it is, so display the child at depth + 1
                Element childElement = (Element) node;
                display(childElement, depth + 1);

                // does the node contain text?
            } else if (node.getNodeType() == Node.TEXT_NODE) {
                // it does, so get the text and display it in quotes
                Text textNode = (Text) node;
                String data = textNode.getTextContent();
                // the string could contain spaces, new lines or tabs on either side of it, so trim these off
                data = data.trim();
                // anything left of the string? if so, display it
                if (data.length() > 0) System.out.printf("\t%s'%s'\n", indent, data);
            }
        }

        // display end of the node
        System.out.printf("%sEND(%s)\n", indent, name);
    }

    public static void main(String[] args) throws Exception {
        // create DocumentBuilder to parse XML document
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // load document and parse to create tree
        File fin = new File("items.xml");
        Document doc = builder.parse(fin);

        // iterate through tree structure
        Element root = doc.getDocumentElement();
        display(root, 0);

        System.out.println();
        System.out.println();

        // obtain all nodes that have the tag name of 'description' and display them
        NodeList descriptions = root.getElementsByTagName("description");
        for (int i = 0; i < descriptions.getLength(); i++)
            display((Element) descriptions.item(i), 0);

    }
}

Example 3

public class Main {
    /**
     * Create an empty document.
     * @return The newly created document.
     * @throws Exception If cannot create the document for some reason.
     */
    public static Document createDoc() throws Exception {
        // create DocumentBuilder to parse XML document
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // create empty document
        Document doc = builder.newDocument();

        return doc;
    }

    /**
     * <p>Save the document to a file with the given filename.</p>
     * <p>You will be given the code for this method in a test, so you do not need to memorise it.</p>
     * @param doc The document to save.
     * @param filename The filename of the file to which the document will be saved.
     * @throws Exception If something goes wrong, e.g. invalid filename, not enough space, etc.
     */
    public static void saveDoc(Document doc, String filename) throws Exception {
        // obtain serializer
        DOMImplementation impl = doc.getImplementation();
        DOMImplementationLS implLS = (DOMImplementationLS) impl.getFeature("LS", "3.0");
        LSSerializer ser = implLS.createLSSerializer();
        ser.getDomConfig().setParameter("format-pretty-print", true);

        // create file to save too
        FileOutputStream fout = new FileOutputStream(filename);

        // set encoding options
        LSOutput lsOutput = implLS.createLSOutput();
        lsOutput.setEncoding("UTF-8");

        // tell to save xml output to file
        lsOutput.setByteStream(fout);

        // FINALLY write output
        ser.write(doc, lsOutput);

        // close file
        fout.close();
    }

    /**
     * <p>Creates, populates and returns an element that simply contains a text node. The element will have the
     * following format: &lt;name&gt;text&lt;/name&gt;</p>
     * @param doc The document to which this element belongs.
     * @param name The tag name for the element.
     * @param text The text contained by the element.
     * @return The newly created element.
     */
    public static Element createTextElement(Document doc, String name, String text) {
        Text textNode = doc.createTextNode(text);
        Element element = doc.createElement(name);
        element.appendChild(textNode);
        return element;
    }

    /**
     * <p>Creates, populates and returns a product element in the form <pre>
     *     &lt;product&gt;
     *          &lt;description&gt;description&lt;/product&gt;
     *          &lt;price&gt;price&lt;/price&gt;
     *     &lt;/product&gt;
     * </pre></p>
     * @param doc The document to which the element belongs.
     * @param description The description of the product.
     * @param price The price of the product.
     * @return The newly created product.
     */
    public static Element createProduct(Document doc, String description, String price) {
        Element product = doc.createElement("product");
        product.appendChild(createTextElement(doc, "description", description));
        product.appendChild(createTextElement(doc, "price", price));
        return product;
    }

    /**
     * <p>Creates, populates and returns an item element. The element will contain a nested
     * product element, as well as a &lt;quantity&gt;quantity&lt;quantity&gt; element.</p>
     * @param doc The document to which the element belongs.
     * @param description The description of the product.
     * @param price The price of the product.
     * @param quantity The number of items of the given product type.
     * @return The newly created item.
     */
    public static Element createItem(Document doc, String description, String price, String quantity) {
        Element item = doc.createElement("item");
        item.appendChild(createProduct(doc, description, price));
        item.appendChild(createTextElement(doc, "quantity", quantity));
        return item;
    }

    /**
     * Creates and returns a document containing a number of items.
     */
    public static Document createItemDocument() throws Exception {
        Document doc = createDoc();

        Element items = doc.createElement("items");
        items.appendChild(createItem(doc, "Caramelo Bears", "5.25", "4"));
        items.appendChild(createItem(doc, "Jelly Tots", "1.24", "1000"));
        items.appendChild(createItem(doc, "Smarties", "2.35", "60"));
        doc.appendChild(items);

        return doc;
    }

    public static void main(String[] args) throws Exception {
        saveDoc(createItemDocument(), "output.xml");
    }
}

XML notes

Element Traverse

public static void display(Element element, int depth) {
    // create a string of 'depth' tabs
    String indent = "";
    for (int i = 0; i < depth; i++)
        indent = indent.concat("\t");

    // get the name of the node
    String name = element.getNodeName();

    // display the beginning of the node
    System.out.printf("%sBEGIN(%s)\n", indent, name);

    NodeList nodes = element.getChildNodes();
    // for each node, display it (and children if any)
    for (int i = 0; i < nodes.getLength(); i++) {
        Node node = nodes.item(i);

        // is it a node containing child nodes?
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            // it is, so display the child at depth + 1
            display((Element) node, depth + 1);

            // does the node contain text?
        } else if (node.getNodeType() == Node.TEXT_NODE) {
            // it does, so get the text and display it in quotes
            Text textNode = (Text) node;
            String data = textNode.getTextContent();
            // the string could contain spaces, new lines or tabs on either side of it, so trim these off
            data = data.trim();
            // anything left of the string? if so, display it
            if (data.length() > 0) System.out.printf("\t%s'%s'\n", indent, data);
        }
    }

    // display end of the node
    System.out.printf("%sEND(%s)\n", indent, name);
}

XPath

//1.	What degree is the student registered for?
path.evaluate("/academic_record/student/degree", doc)

//2.	How many years has the student studied?
path.evaluate("count(/academic_record/student/degree)", doc)

//3.	In 2002 what was the mark obtained for the June exams for WRA101?
path.evaluate("/academic_record/record/academic_year[@year=2002]/module[@code='WRA101']/@grade", doc)

//4.	How many exams were written for WRA101 (including supplementary exams)?
path.evaluate("count(/academic_record/record/academic_year/module[@code='WRA101' and @result!='No DP'])", doc)

//5.	How many supplementary exams (for different modules) did the student write?
path.evaluate("count(/academic_record/record/academic_year/module[@code='WRA101' and @result='Supplementary'])", doc)

//6.	In 2003, what is the module code of the 2nd module enrolled for?
path.evaluate("/academic_record/record/academic_year[@year=2003]/module[2]/@code", doc)

Create Existing XML for Querying

private static void CreateAndSaveXML() throws Exception {
    // create DocumentBuilder to parse XML document
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();

    // load document and parse to create tree
    File fin = new File("items.xml");
    Document doc = builder.parse(fin);

    // create xpath object that will allow document to be queried
    XPathFactory xpFactory = XPathFactory.newInstance();
    XPath path = xpFactory.newXPath();

    // query the document and save out nodes that match a specified criteria
    // create document
    Document outputDoc = createDoc();
    // create root element and attach to document
    Element results = outputDoc.createElement("results");
    outputDoc.appendChild(results);
    //NB! Each XML document is a tree, can only have one root

    //XPathExpression expr = path.compile("//description/text()");
    //XPathExpression expr = path.compile("//item/*/description/text()");
    //XPathExpression expr = path.compile("//item");
    XPathExpression expr = path.compile("//item[quantity>=8 and product/price/@currency='ZAR']");

    // NODESET means more than one node will be returned by the query
    NodeList resultSet = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
    System.out.println("# nodes returned by query = " + resultSet.getLength());

    // unfortunately, the foreach notation does not work for the result set :(
    // i.e. for(Node node : resultSet)
    for (int i = 0; i < resultSet.getLength(); i++) {
        // attach item to root (if you want to use a node from one tree in another, you need to clone it first
        // as a node cannot belong to more than one tree at a time)
        Node node = resultSet.item(i).cloneNode(true);
        // associate the node with the output document
        outputDoc.adoptNode(node);
        // now allowed to attach the nodes together
        results.appendChild(node);
    }

    // get old root
    saveDoc(outputDoc, "items-output.xml");
}

Create New Document

public static Document createDoc() throws Exception {
    // create DocumentBuilder to parse XML document
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();

    // create empty document
    Document doc = builder.newDocument();

    return doc;
}

! ! ! ! XML File Building

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.FileOutputStream;

public class Main {
        public static Document createDoc() throws Exception {
        // create DocumentBuilder to parse XML document
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        // create empty document
        Document doc = builder.newDocument();
        return doc;
    }

    public static void saveDoc(Document doc, String filename) throws Exception {
        // obtain serializer
        DOMImplementation impl = doc.getImplementation();
        DOMImplementationLS implLS = (DOMImplementationLS) impl.getFeature("LS", "3.0");
        LSSerializer ser = implLS.createLSSerializer();
        ser.getDomConfig().setParameter("format-pretty-print", true);
        // create file to save too
        FileOutputStream fout = new FileOutputStream("" + filename);
        // set encoding options
        LSOutput lsOutput = implLS.createLSOutput();
        lsOutput.setEncoding("UTF-8");
        // tell to save xml output to file
        lsOutput.setByteStream(fout);
        // FINALLY write output
        ser.write(doc, lsOutput);
        fout.close();
    }

    public static Element createTextElement(Document doc, String name, String text) {
        Text textNode = doc.createTextNode(text);
        Element element = doc.createElement(name);
        element.appendChild(textNode);
        return element;
    }

    public static Element createProduct(Document doc, String description, String price) {
        Element product = doc.createElement("product");
        product.appendChild(createTextElement(doc, "description", description));
        product.appendChild(createTextElement(doc, "price", price));
        return product;
    }

    public static Element createItem(Document doc, String description, String price, String quantity) {
        Element item = doc.createElement("item");
        item.appendChild(createProduct(doc, description, price));
        item.appendChild(createTextElement(doc, "quantity", quantity));
        return item;
    }

    /**
     * Creates and returns a document containing a number of items.
     */
    public static Document createItemDocument() throws Exception {
        Document doc = createDoc();

        Element items = doc.createElement("items");
        items.appendChild(createItem(doc, "Caramelo Bears", "5.25", "4"));
        items.appendChild(createItem(doc, "Jelly Tots", "1.24", "1000"));
        items.appendChild(createItem(doc, "Smarties", "2.35", "60"));
        doc.appendChild(items);

        return doc;
    }

    public static void main(String[] args) throws Exception {
        saveDoc(createItemDocument(), "output.xml");
    }
}

WRAP301 Lecture 3

Generics and Lambdas

Example 1: Stack

Classes: Stack, Main

Stack

public class Stack<T> {
    protected class Node {
        public T value;
        public Node prev;

        public Node(T value, Node prev) {
            this.value = value;
            this.prev = prev;
        }
    }

    private Node top = null;

    public void push(T value) {
        top = new Node(value, top);
    }

    public T pop() {
        if (top == null)
            return null;
        else {
            Node temp = top;
            top = top.prev;
            return temp.value;
        }
    }

    public int size() {
        int count = 0;

        for(Node temp = top; temp != null; temp = temp.prev, count++);

        return count;
    }

    @Override
    public String toString() {
        String s = "-> ";
        Node temp = top;

        while (temp != null) {
            s = s.concat(temp.value.toString());
            temp = temp.prev;
            if (temp != null)
                s = s.concat(" - ");
        }

        return s;
    }
}

Main

public class Main {
    public static void main(String[] args) {
        // create a stack that can contain Strings (and/or descendants)
        Stack<String> strStack = new Stack<String>();
        strStack.push("Java");
        strStack.push("is");
        strStack.push("great");

        /*
        the following line won't compile due to type checking:
        strStack.push(10);
        */

        System.out.printf("String stack is of size %d and contents = %s.\n", strStack.size(), strStack);
        String topString = strStack.pop();
        System.out.printf("The topmost value of the string stack is '%s'.\n", topString);
        System.out.printf("String stack after popping = %s.\n", strStack);

        // create a stack that can contains Integers
        Stack<Integer> intStack = new Stack<Integer>();
        intStack.push(10);
        intStack.push(11);
        intStack.push(12);
        intStack.push(13);

        /*
        the following line won't compile due to type checking:
        intStack.push("hello");
        */

        System.out.printf("Integer stack is of size %d and contents = %s.\n", intStack.size(), intStack);
        int topInt = intStack.pop();
        System.out.printf("The topmost value of the string stack is '%s'.\n", topInt);
    }
}

Example 2: Key-Pair Class

Classes: Pair, Pairs, Main

Pair

/**
 * A class that contains a simple key-value pair.
 * @param <K> The type of a key object.
 * @param <V> The type of a value object.
 */
public class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "(" + key + " = " + value + ")";
    }
}

Pairs

/**
 * A collection of key value pairs, where a value can be looked up using a unique key. This
 * is a <b>really</b> simple version of a dictionary/hash table. If you want a more complete
 * implementation, check out the Map interface and HashMap class.
 *
 * @param <K> The type of the keys.
 * @param <V> The type of the values.
 */
public class Pairs<K, V> {
    private ArrayList<Pair<K, V>> pairs;

    /**
     * Create an empty collection of pairs.
     */
    public Pairs() {
        pairs = new ArrayList<Pair<K, V>>();
    }

    /**
     * Is there a pair with the given key?
     *
     * @param key The key to be searched for.
     * @return true if the a pair exists with the given key, false otherwise.
     */
    public boolean contains(K key) {
        boolean found = false;

        for (Pair<K, V> pair : pairs) {
            if (pair.getKey().equals(key))
                found = true;
        }

        return found;
    }

    /**
     * Adds a new pair to the pairs, if there is not already a pair with the given key, otherwise updates the value associated with the key.
     *
     * @param key   The key of the pair.
     * @param value The value associated with the key.
     */
    public void set(K key, V value) {
        if (!contains(key)) {
            // doesn't contain the key, so set a new pairing
            pairs.add(new Pair<K, V>(key, value));
        } else {
            // key exists, so update the value paired with it
            for (Pair<K, V> pair : pairs) {
                if (pair.getKey().equals(key))
                    pair.setValue(value);
            }
        }
    }

    /**
     * Retrieves the value associated with the given key. If no value is found, then the default value is returned.
     *
     * @param key          The key being searched for.
     * @param defaultValue The value returned if there is no key-value pair.
     * @return The value associated with the key, if it exists, otherwise the default value.
     */
    public V get(K key, V defaultValue) {
        V value = defaultValue;

        for (Pair<K, V> pair : pairs) {
            if (pair.getKey().equals(key))
                value = pair.getValue();
        }

        return value;
    }

    @Override
    public String toString() {
        return pairs.toString();
    }
}

Main

public class Main {

    /**
     * Generates a collection of (Integer, V) pairs from a variable length list of parameters.
     * @param values The values to be added to the new pairings.
     * @param <V> The type of the pair value.
     * @return A pairs collection containing a collection of incrementally numbered pairs.
     */
    public static <V> Pairs<Integer, V> generate(V... values) {
        // create a new collection of pairs, where the key is of type Integer and the values are of unknown type.
        Pairs<Integer, V> pairs = new Pairs<>();

        Integer i = 0;
        // for every value passed in as a parameter
        for (V value : values) {
            // create a key, value pair
            pairs.set(i, value);
            i++;
        }

        return pairs;
    }

    public static void main(String[] args) {
        // Int -> String pairs
        Pairs<Integer, String> pairs = new Pairs<>();
        pairs.set(1, "one");
        pairs.set(2, "two");
        pairs.set(3, "three");
        pairs.set(1, "ONE");
        System.out.println("pairs = " + pairs);

        // note: no type casting needed!
        String value = pairs.get(1, "Unknown");
        System.out.println("value = " + value);

        value = pairs.get(10, "Unknown");
        System.out.println("value = " + value);

        // using the static method
        Pairs<Integer, Integer> fibs = generate(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144);
        System.out.println("pairs = " + fibs);
        Integer f = fibs.get(10, -1);
        System.out.printf("f(10) = %d\n", f);
    }
}

Example 3: Lambdas

Classes: Example, Main, People, Person, PersonSelector

Person

public class Person {
    private String surname;
    private String firstName;
    private int age;

    public Person(String surname, String firstName, int age) {
        this.surname = surname;
        this.firstName = firstName;
        this.age = age;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return firstName + " " + surname + ", age " + age;
    }
}

Person Selector

public interface PersonSelector {
    public boolean isSelected(Person person);
}

People

public class People {
    private ArrayList<Person> people;

    public People() {
        people = new ArrayList<>();

        // add some dummy data
        people.add(new Person("Doe", "Jane", 17));
        people.add(new Person("Doe", "John", 21));
        people.add(new Person("Zee", "Jay", 45));
        people.add(new Person("Jay", "Dee", 30));
        people.add(new Person("Bee", "Jyujyu", 16));
        people.add(new Person("Duud", "Ald", 90));
    }

    public void displayYoungerThan25() {
        for(Person person : people)
            if(person.getAge() < 25)
                System.out.println(person);
    }

    public void displaySurnameStartsWithD() {
        for(Person person : people)
            if(person.getSurname().startsWith("D"))
                System.out.println(person);
    }

    public void displayYoungerThan25AndSurnameStartsWithD() {
        for(Person person : people)
            if((person.getAge() < 25) && (person.getSurname().startsWith("D")))
                System.out.println(person);
    }

    public void display(PersonSelector selector) {
        for(Person person : people)
            if(selector.isSelected(person))
                System.out.println(person);
    }
}

Main

public class Main {
    public static void main(String[] args) {
        People people = new People();

        /* selecting items to display using built in methods (not scalable!) */
        System.out.println("Starts with D (built-in)");
        people.displaySurnameStartsWithD();
        System.out.println();

        System.out.println("Younger than 25 (built-in)");
        people.displayYoungerThan25();
        System.out.println();

        System.out.println("Starts with D & younger than 25 (built-in)");
        people.displayYoungerThan25AndSurnameStartsWithD();
        System.out.println();

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // display selected people using an anonymous class - more scalable than built-in
        // just SO verbose for one method!
        System.out.println("Starts with D (anonymous)");
        people.display(new PersonSelector() {
            @Override
            public boolean isSelected(Person person) {
                return person.getSurname().startsWith("D");
            }
        });
        System.out.println();

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // display selected people using a named local class - even MORE verbose
        class SelectYoungerThan25 implements PersonSelector {
            @Override
            public boolean isSelected(Person person) {
                return person.getAge() < 25;
            }
        };
        // in this case can extend the named local class, since HAS a name
        // these classes ONLY exists within the main method, i.e. they cannot
        // be instantiated in another method
        class SelectStartsWithDAndYoungerThan25 extends SelectYoungerThan25 {
            @Override
            public boolean isSelected(Person person) {
                return super.isSelected(person) && person.getSurname().startsWith("D");
            }
        }

        System.out.println("Younger than 25 and starts with D (named local class)");
        people.display(new SelectStartsWithDAndYoungerThan25());
        System.out.println();

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // display selected people using a lambda expression
        System.out.println("Younger than 25 (lambda)");
        people.display(person -> person.getAge() < 25);
        System.out.println();

        // combine selectors
        PersonSelector selName = person -> person.getSurname().startsWith("D");
        PersonSelector selAge = person -> person.getAge() < 25;

        System.out.println("Starts with D & younger than 25 (lambda)");
        people.display(person -> selName.isSelected(person) && selAge.isSelected(person));
        System.out.println();

        // a lambda with a multi-instruction body
        // note the use of RETURN (not needed before)
        Example x = (n) -> {
            int count = 0;
            for(int i = 2; i <= n/2; i++)
                if((n % i) == 0)
                    count++;
            return count;
        };

        System.out.println("number of factors = " + x.count(100));
    }
}

**Example**
java @FunctionalInterface public interface Example { int count(int x); } ```

Example 4: Property Changed Listener

Classes: Person, Property, PropertyChangeListener, Main

Person

public class Person {
    public Property<String> name = new Property<>("Bob");
    public Property<Integer> age = new Property<>(20);
}

Property

public class Property<T> {
    private T value;
    private ArrayList<PropertyChangeListener<T>> listeners = null;

    public Property(T initialValue) {
        value = initialValue;
    }

    public void addListener(PropertyChangeListener<T> listener) {
        if(listeners == null)
            listeners = new ArrayList<>();

        listeners.add(listener);
    }

    public void removeListener(PropertyChangeListener<T> listener) {
        if(listeners != null)
            listeners.remove(listener);
    }

    public T get() {
        return value;
    }

    public void set(T newValue) {
        T oldValue = value;
        value = newValue;

        // notify listeners of changes
        if(listeners != null) {
            for(PropertyChangeListener<T> listener : listeners)
                listener.propertyChanged(this, oldValue, newValue);
        }
    }
}

PropertyChangeListener

@FunctionalInterface
public interface PropertyChangeListener<T> {
    void propertyChanged(Property<T> property, T oldValue, T newValue);
}

Main

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    // note signature (if you ignore the name) matches the PropertyChangeListener<T> interface
    public void handleNameChanged(Property<String> property, String oldValue, String newValue) {
        System.out.println("Main.handle name change 1. Name = " + newValue);
    }

    public void handleNameChanged2(Property<String> property, String oldValue, String newValue) {
        System.out.println("Main.handle name change 2. Old name = " + oldValue + ", new name = " + newValue);
    }

    public void handleAgeChanged(Property<Integer> property, Integer oldValue, Integer newValue) {
        System.out.println("Main.handle age change. Age = " + newValue);
    }

    public static void staticHandleNameChanged(Property<String> property, String oldValue, String newValue) {
        System.out.println("Static.handle name change. Name = " + newValue);
    }

    public Main() {
        Person bob = new Person();

        // add Lambda expression listeners
        // attach listeners to bob's properties (note the types of the parameters)!
        // when typing, press CTRL+SHIFT+Space to be presented with basic code
        bob.name.addListener((property, oldValue, newValue) -> System.out.println("New name = " + newValue));
        bob.age.addListener((property, oldValue, newValue) -> System.out.println("New age = " + newValue));

        // just for fun, add a second lambda expression listener to be notified about name changes
        bob.name.addListener((property, oldValue, newValue) -> System.out.printf("Name was '%s', is now '%s'\n", oldValue, newValue));

        // using Lambdas, point to this object's methods that match the signatures
        bob.name.addListener(this::handleNameChanged);
        bob.name.addListener(this::handleNameChanged2);
        bob.age.addListener(this::handleAgeChanged);

        // using a static method
        bob.name.addListener(Main::staticHandleNameChanged);

        // now trigger the change listeners (note the parameter type)
        bob.name.set("Billy-Bob");
        // happy birthday Billy-Bob!!
        bob.age.set(21);
    }
}

Example 5: Method Call

Method Call

public interface MethodCall {
    void execute(SomeClass obj, int n);
}

Object Method Call

public interface ObjectMethodCall {
    void execute(int n);
}

SomeClass

public class SomeClass {
    private String name;

    public SomeClass(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }

    public void f(int x) {
        System.out.printf("Calling f(%d) on %s\n", x, this);
    }

    public void g(int a) {
        System.out.printf("Calling g(%d) on %s\n", a, this);
    }

    public void h(int n) {
        System.out.printf("Calling h(%d) on %s\n", n, this);
    }
}

Main

public class Main {
    public static void main(String[] args) {
        // object instances
        SomeClass c1 = new SomeClass("c1");
        SomeClass c2 = new SomeClass("c2");

        // lambda expressions that call a specific execute on an object
        System.out.println("Calling a specific execute on different objects");
        MethodCall f = (obj, n) -> obj.f(n); // using "standard" way
        MethodCall g = SomeClass::g; // using "sugared" way

        // calling the same execute on different objects
        f.execute(c1, 10);  // call f(n) on c1
        f.execute(c2, 15);  // call f(n) on c2

        g.execute(c1, 5);   // call g(n) on c1
        g.execute(c2, 10);  // call g(n) on c2

        System.out.println();

        // call a specific execute on a specific object - note no object passed in as a parameter!
        System.out.println("Calling a specific execute on a specific object");
        ObjectMethodCall c1f = c1::f;
        ObjectMethodCall c2g = c2::g;
        ObjectMethodCall c1h = c1::h;

        c1f.execute(10);     // call c1.f()
        c2g.execute(20);     // call c2.g()
        c1h.execute(15);     // call c1.h()
    }
}

GitHub – LadySith

Sith

Lusaka, Zambia

Computer Science student and a bunch of other things.