com.jaspersoft.studio.custom.adapter.controls.DynamicControlComposite.java Source code

Java tutorial

Introduction

Here is the source code for com.jaspersoft.studio.custom.adapter.controls.DynamicControlComposite.java

Source

/*******************************************************************************
 * Copyright (C) 2005 - 2014 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com.
 * 
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 * 
 * 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
 ******************************************************************************/
package com.jaspersoft.studio.custom.adapter.controls;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import net.sf.jasperreports.data.DataAdapter;
import net.sf.jasperreports.engine.JasperReportsContext;

import org.apache.commons.beanutils.BeanUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.jaspersoft.studio.data.ADataAdapterComposite;
import com.jaspersoft.studio.data.DataAdapterDescriptor;

/**
 * A dynamic configuration composite that create the control to edit a data adapter
 * from the castor mapping file of the data adapter itself
 * 
 * @author Orlandin Marco
 *
 */
public abstract class DynamicControlComposite extends ADataAdapterComposite {

    /**
     * Key value for the type of a field
     */
    protected static final String TYPE_KEY = "type";

    /**
     * Key value for the name of a field
     */
    protected static final String BIND_KEY = "bind";

    /**
     * The descriptor of the data adapter
     */
    protected DataAdapterDescriptor dataAdapterDescriptor;

    /**
     * The list of controls generated to edit the data adapter
     */
    protected List<Control> generatedControls = new ArrayList<Control>();

    /**
     * Construct the class and initialize the controls
     * 
     * @param parent the parent of the controls
     * @param style the style for the controls
     * @param context the current JasperReportsContext
     */
    protected DynamicControlComposite(Composite parent, int style, JasperReportsContext context) {
        super(parent, style, context);
        setLayout(new GridLayout(2, false));
    }

    /**
     * Return the castor mapping file definition of the data adapter 
     * 
     * @return the castor mapping path in the workspace of the data adapter or null if it can't be found
     */
    protected String getXmlDefinitionLocation() {
        Properties props = new Properties();
        try {
            props.load(dataAdapterDescriptor.getClass().getResourceAsStream("/jasperreports_extension.properties"));
            for (Object property : props.keySet()) {
                if (property.toString().startsWith("net.sf.jasperreports.extension.castor.mapping")) {
                    return props.getProperty(property.toString());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Create a control inside the composite. The type of control change to reflect the type
     * 
     * @param type the type of control to create. Supported control are int, boolean, float and string
     * @param bindName the name of the field binded to the control
     * @param label the label of the control
     * @return the text to place in the template to generate the control
     */
    protected void createDynamicControl(String type, String bindName, String label) {
        if (type.equals("int")) {
            Label lbl = new Label(this, SWT.NONE);
            lbl.setText(label);
            Spinner control = new Spinner(this, SWT.BORDER);
            control.setData(BIND_KEY, bindName);
            control.setData(TYPE_KEY, type);
            control.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
            generatedControls.add(control);
        } else if (type.equals("boolean")) {
            Label lbl = new Label(this, SWT.NONE);
            lbl.setText(label);
            Button control = new Button(this, SWT.CHECK);
            control.setData(BIND_KEY, bindName);
            control.setData(TYPE_KEY, type);
            generatedControls.add(control);
        } else if (type.equals("float")) {
            Label lbl = new Label(this, SWT.NONE);
            lbl.setText(label);
            Text control = new Text(this, SWT.BORDER);
            control.setData(BIND_KEY, bindName);
            control.setData(TYPE_KEY, type);
            control.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
            control.addVerifyListener(new VerifyListener() {
                @Override
                public void verifyText(VerifyEvent e) {
                    Text text = (Text) e.getSource();
                    // get old text and create new text by using the
                    // VerifyEvent.text
                    final String oldS = text.getText();
                    String newS = oldS.substring(0, e.start) + e.text + oldS.substring(e.end);
                    boolean isFloat = true;
                    try {
                        Float.parseFloat(newS);
                    } catch (NumberFormatException ex) {
                        isFloat = false;
                    }
                    if (!isFloat)
                        e.doit = false;
                }
            });
            generatedControls.add(control);
        } else if (type.equals("string")) {
            Label lbl = new Label(this, SWT.NONE);
            lbl.setText(label);
            Text control = new Text(this, SWT.BORDER);
            control.setData(BIND_KEY, bindName);
            control.setData(TYPE_KEY, type);
            control.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        }
    }

    /**
     * Generate the control to handle a list of vales
     * 
     * @param type the type of the values
     * @param bindName the name of the collection field binded to this list
     * @param label label of the list control
     */
    protected void createDynamicArray(String type, String bindName, String label) {
        new Label(this, SWT.NONE).setText(label);
        PropertyListControl listControl = new PropertyListControl(this, type);
        listControl.setData(BIND_KEY, bindName);
        listControl.setData(TYPE_KEY, "collection");
        listControl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        generatedControls.add(listControl);
    }

    /**
     * Given a field node of the castor mapping file it return
     * the first bind-xml node
     * 
     * @param parent the field node of the castor mapping 
     * @return the first children bind-xml node
     */
    private Node getBindNode(Node parent) {
        NodeList children = parent.getChildNodes();
        for (int i = 0; i < children.getLength(); ++i) {
            Node child = children.item(i);
            if (child.getNodeName().equals("bind-xml"))
                return child;
        }
        return null;
    }

    /**
     * Read the content of a castor mapping file and create an appropriate
     * control for every field inside the node list
     * 
     * @param fieldNodes list of the children of the root node of the castor mapping file
     */
    protected void createDynamicControls(NodeList fieldNodes) {
        for (int i = 0; i < fieldNodes.getLength(); ++i) {
            Node fieldNode = fieldNodes.item(i);
            if (fieldNode.getNodeName().equals("field")) {
                String nameAttribute = fieldNode.getAttributes().getNamedItem("name").getNodeValue();
                //name and class are handled by default
                if (!nameAttribute.equals("class") && !nameAttribute.equals("name")) {
                    String type = fieldNode.getAttributes().getNamedItem("type").getNodeValue();
                    Node collectionNode = fieldNode.getAttributes().getNamedItem("collection");
                    boolean isArray = collectionNode != null
                            && collectionNode.getNodeValue().toLowerCase().equals("arraylist");
                    Node bindNode = getBindNode(fieldNode);
                    if (bindNode != null) {
                        String name = fieldNode.getAttributes().getNamedItem("name").getNodeValue();
                        String bindName = bindNode.getAttributes().getNamedItem("name").getNodeValue();
                        if (isArray) {
                            createDynamicArray(type, bindName, name);
                        } else {
                            createDynamicControl(type, bindName, name);
                        }
                    }
                }
            }
        }
    }

    /**
     * Search a castor mapping file inside the data adapter jar and if it is found create the controls
     * to edit it
        
     */
    protected void createDynamicControls() {
        String xmlDefinition = getXmlDefinitionLocation();
        if (xmlDefinition != null) {
            DataAdapter adapter = dataAdapterDescriptor.getDataAdapter();
            InputStream is = dataAdapterDescriptor.getClass().getResourceAsStream("/" + xmlDefinition);
            if (null != is) {
                try {
                    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                    dbf.setValidating(false);
                    dbf.setIgnoringComments(true);
                    dbf.setNamespaceAware(false);
                    DocumentBuilder builder = dbf.newDocumentBuilder();
                    builder.setEntityResolver(new EntityResolver() {
                        @Override
                        public InputSource resolveEntity(String publicId, String systemId)
                                throws SAXException, IOException {
                            if (systemId.contains("http://castor.org/mapping.dtd")) {
                                return new InputSource(new StringReader(""));
                            } else {
                                return null;
                            }
                        }
                    });

                    Document document = builder.parse(is);
                    Node mapNode = document.getDocumentElement();
                    if (mapNode.getNodeName().equals("mapping")) {
                        NodeList adapterNodes = mapNode.getChildNodes();
                        for (int j = 0; j < adapterNodes.getLength(); ++j) {
                            Node adapterNode = adapterNodes.item(j);
                            if (adapterNode.getNodeName().equals("class")) {
                                String classAttribute = adapterNode.getAttributes().getNamedItem("name")
                                        .getNodeValue();
                                if (classAttribute != null && classAttribute.equals(adapter.getClass().getName())) {
                                    createDynamicControls(adapterNode.getChildNodes());
                                    is.close();
                                    return;
                                }
                            }
                        }
                    }
                } catch (Exception ex) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    ex.printStackTrace();
                }
            }

        }
    }

    /**
     * Create the controls
     */
    protected void initComponents() {
        //dispose the old components
        for (Control control : getChildren()) {
            control.dispose();
        }
        generatedControls.clear();
        createDynamicControls();
    }

    /**
     * Set the data adapter descriptor from outside and bind the created controls to it
     */
    @Override
    public void setDataAdapter(DataAdapterDescriptor dataAdapterDescriptor) {
        this.dataAdapterDescriptor = dataAdapterDescriptor;
        DataAdapter dataAdapter = dataAdapterDescriptor.getDataAdapter();
        initComponents();
        bindWidgets(dataAdapter);
    }

    /**
     * Given the data adapter uses it to initialize the values of the controls to
     * reflect the current configuration
     *  
     * @param dataAdapter the not null data adapter
     */
    protected void updateAdapterSettings(DataAdapter dataAdapter) {
        for (Control control : generatedControls) {
            String methodName = (String) control.getData(BIND_KEY);
            String type = (String) control.getData(TYPE_KEY);
            try {
                if (type.equals("string")) {
                    Text text = (Text) control;
                    BeanUtils.setProperty(dataAdapter, methodName, text.getText());
                } else if (type.equals("float")) {
                    Text text = (Text) control;
                    BeanUtils.setProperty(dataAdapter, methodName, text.getText());
                } else if (type.equals("int")) {
                    Spinner spinner = (Spinner) control;
                    BeanUtils.setProperty(dataAdapter, methodName, String.valueOf(spinner.getSelection()));
                } else if (type.equals("boolean")) {
                    Button button = (Button) control;
                    BeanUtils.setProperty(dataAdapter, methodName, String.valueOf(button.getSelection()));
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

    /**
     * Bind the data adapter to the widgets to have that every change on the
     * controls widgets reflected on the data adapter
     * 
     * @param dataAdapter the data adapter
     */
    @Override
    protected void bindWidgets(DataAdapter dataAdapter) {
        final DataAdapter adapter = (DataAdapter) dataAdapter;

        //Listeners used to detect the change of the value of a control
        ModifyListener valueChangedListener = new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent e) {
                updateAdapterSettings(adapter);
            }
        };
        SelectionAdapter buttonChangedListener = new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                updateAdapterSettings(adapter);
            }
        };

        //Set the initial value on every control reading it from the data adapter
        //and add the listener to detect the changes done by the user
        for (Control control : generatedControls) {
            String methodName = (String) control.getData(BIND_KEY);
            String type = (String) control.getData(TYPE_KEY);
            try {
                String result = BeanUtils.getProperty(adapter, methodName);
                if (type.equals("string") || type.equals("float")) {
                    Text text = (Text) control;
                    text.setText(result != null ? result : "");
                    text.addModifyListener(valueChangedListener);
                } else if (type.equals("int")) {
                    Spinner spinner = (Spinner) control;
                    if (result != null)
                        spinner.setSelection(Integer.parseInt(result));
                    spinner.addModifyListener(valueChangedListener);
                } else if (type.equals("boolean")) {
                    Button button = (Button) control;
                    if (result != null)
                        button.setSelection(Boolean.parseBoolean(result));
                    button.addSelectionListener(buttonChangedListener);
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }
}