Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g Release 3 (10.1.3.0) Part Number B25947-02 |
|
|
View PDF |
If you need data controls beyond those that are provided by JDeveloper, you can create your own. ADF supports two main ways to create data controls:
Create a JavaBean to represent the data source.
Create a data control adapter for the data source type.
This chapter describes the second option: creating a data control adapter. For information about data controls, see Chapter 1, "Introduction to Oracle ADF Applications".
This chapter contains the following topics:
Section 31.1, "Introduction to the Simple CSV Data Control Adapter"
Section 31.2, "Overview of Steps to Create a Data Control Adapter"
Section 31.9, "Package and Deploy Your Adapter to JDeveloper"
This chapter shows a simple CSV data control adapter as an example of a custom data control adapter. This adapter is a simplified version of the CSV data control adapter that ships with JDeveloper.
The chapter describes what the simple CSV data control adapter does and the classes that make up the adapter.
The simple CSV data control adapter retrieves comma-separated values from a file and displays them on a page. To use the adapter in JDeveloper, you can do one of the following:
right-click a node that represents a CSV file and choose "Create Data Control" from the context menu
drag and drop a node on the Data Control Palette
In either case, the node must map to a CSV text file, and the name of the file must have a .csv
extension. You do not have to enter any metadata because the simple CSV data control adapter extracts the metadata from the node.
After you create a data control using the simple CSV adapter, the data control appears in the Data Control Palette. You can then drag and drop it onto a view page.
To simplify some details, the simple CSV adapter hardcodes the following items:
The fields in the CSV file are comma-separated.
The delimiter character is the double-quote character.
The CSV file uses UTF-8 encoding.
The first line in the file specifies column names.
The name of the CSV file must have a .csv
extension.
(The CSV adapter that ships with JDeveloper enables you to set these values.)
When you create a data control adapter, you create it so that it represents a source type, not a source instance. In the case of the CSV adapter, the source type is CSV files. To specify a specific data instance, for example, a particular CSV file, the user creates a data control with the help of the data control adapter and associates the instance with metadata. The metadata specifies the data for the instance. In the case of the simple CSV adapter, the metadata includes the path to a specific CSV file.
The responsibilities of a data control adapter include:
Providing metadata for the data control instance
Creating a data control instance using the stored metadata during runtime
Data control adapters run within the adapter framework. The adapter framework takes care of storing the metadata, integrating the data control adapter with the ADF lifecycle, and integrating with JDeveloper during design time.
To create data control adapters:
Create classes to extend abstract classes and implement interfaces in the adapter framework. These classes are used during design time and runtime. You have to create three classes as described in these sections:
You can also create additional classes as required by your adapter. For the simple CSV adapter, it includes two additional classes: CSVHandler
and CSVParser
. These classes are shown in Section 31.6, "Create any Necessary Supporting Classes".
Create a definition file, adapter-definition.xml
, to register your adapter with ADF. This file contains the class name of your adapter implementation and references the libraries that your adapter needs to run. See Section 31.7, "Create an XML File to Define Your Adapter".
Install your data control adapter in JDeveloper by packaging your class files and the definition file in a JAR file and placing the JAR file in JDeveloper's classpath. See Section 31.9, "Package and Deploy Your Adapter to JDeveloper".
Invoking Your Adapter
After installing your data control adapter in JDeveloper, you can invoke it by right-clicking a node in JDeveloper that your data control adapter supports and selecting "Create Data Control" from the context menu. The data control adapter declares the node types that it supports in its adapter-definition.xml
configuration file (described in Section 31.7, "Create an XML File to Define Your Adapter").
For example, if your adapter supports database connection nodes, when you right-click on a database connection, then you can select Create Data Control from the context menu to invoke your adapter.
Note that this chapter does not cover how to create a wizard, or how to pass values from a wizard to your adapter.
Implementing the AbstractAdapter
class is optional. It is required only if you want to enable the user to create a data control by dragging and dropping a node onto the Data Control Palette. In this case, the dropped node represents the data source associated with the data control that you are creating. If you do not want this feature, you do not have to implement this class. For example, the CSV data control adapter that ships with JDeveloper does not implement this class because it does not support the drag-and-drop operation. Instead, this adapter displays a wizard to collect information from the user.
The simple CSV adapter implements the AbstractAdapter
. When the user drags and drops a node onto the Data Control Palette, JDeveloper checks to see which adapter can handle the type of node that was dropped. You specify the node types that your adapter can handle in the adapter-definition.xml
file. This file is used to register your adapter with JDeveloper. See Section 31.7, "Create an XML File to Define Your Adapter" for details about this file.
In your class, you have to implement some methods in the AbstractAdapter
class, as described in these sections:
The abstract class oracle.adf.model.adapter.AbstractAdapter
is located in the JDEV_HOME/bc4j/lib/adfm.jar
file.
Example 31-1 shows an outline of a class that implements the AbstractAdapter
class.
Example 31-1 Outline for Class That Implements AbstractAdapter
import oracle.adf.model.adapter.AbstractAdapter; import oracle.adf.model.adapter.DTContext; import oracle.adf.model.adapter.AbstractDefinition; public class MyAdapter extends AbstractAdapter { public void initialize(Object sourceObj, DTContext ctx) { // you need to implement this method. // see Section 31.3.4, "Implementing the initialize Method". } public boolean invokeUI() { // you need to implement this method. // see Section 31.3.5, "Implementing the invokeUI Method". } public AbstractDefinition getDefinition() { // you need to implement this method. // see Section 31.3.6, "Implementing the getDefinition Method". } }
Example 31-2 shows the complete source for the SampleDCAdapter
class. This is the class that implements AbstractAdapter
for the simple CSV adapter. Subsequent sections describe the methods in this class.
Example 31-2 Complete Source for SampleDCAdapter
package oracle.adfinternal.model.adapter.sample; import java.net.URL; import oracle.adf.model.adapter.AbstractAdapter; import oracle.adf.model.adapter.AbstractDefinition; import oracle.adf.model.adapter.DTContext; import oracle.ide.Context; public class SampleDCAdapter extends AbstractAdapter { // JDev Context private Context mJdevCtx = null; // Source object of data private Object mSrc = null; // Source Location private String mSrcLoc = null; // data control name private String mDCName = null; // data control definition private AbstractDefinition mDefinition = null; public SampleDCAdapter() { } /** * Initializes the adapter from a source object. * <p> * The source object can be different thing depending on the context of the * design time that the adapter is used in. For JDeveloper, the object will * be a JDeveloper node. * </p> * <p> * Adapter implementations will check the <code>"ctx"</code> parameter to * get the current design time context. The source object will be used to * extract the information for the data source. * </p> * @param sourceObj Object that contains information about the data source * that will be used to define the data control. * @param ctx Current design time context. */ public void initialize(Object sourceObj, DTContext ctx) { mSrc = sourceObj; mJdevCtx = (Context) ctx.get(DTContext.JDEV_CONTEXT); } /** * Invlokes the UI at the design time. * <p> * This method is a call back from the JDeveloper design time environment to * the adapters to bring up any UI if required to gather information about * the data source they represent. * </p> * * @return false if the user cancels the operation. The default retrun value * is true. */ public boolean invokeUI() { // First check if this is a JDev environment. if (mJdevCtx != null && mSrc != null) { if (extractDataSourceInfo(mSrc)) { SampleDCDef def = new SampleDCDef(mSrcLoc,mDCName); mDefinition = def; return true; } return false; } return false; } /** * <p> * The Definition instance obtained can be used by the ADF design time to * capture the data control metadata. *</p> * * @return The definition instance describing the data control design time. */ public AbstractDefinition getDefinition() { return mDefinition; } /** * @param source the data source object. * @return false if data type is unknown. */ public boolean canCreateDataControl(Object source) { return extractDataSourceInfo(source); } /** * Extracts information from a data source. This method extracts name * from the object. * @param obj the data source object. */ private boolean extractDataSourceInfo(Object obj) { mDCName = "SampleDC"; // See if the node dropped is a text node of CSV type. // We will assume that the CSV data file must end with .csv if (obj instanceof oracle.ide.model.TextNode) { oracle.ide.model.TextNode tn = (oracle.ide.model.TextNode) obj; URL url = tn.getURL(); String loc = url.getFile(); // Check if the file has a matching extension if (loc.endsWith(".csv")) { mSrcLoc = loc; String path = url.getPath(); int index = path.lastIndexOf('/'); if (index != -1) { String fileName = path.substring(index+1); int dotIndex = fileName.lastIndexOf('.'); mDCName = fileName.substring(0,dotIndex); } return true; } } return false; } }
The framework calls the initialize
method when the user drags and drops a node onto the Data Control Palette. The method has the following signature:
The sourceObj
parameter specifies the node that was dropped. You can check this to ensure that the node type is something your adapter can handle.
The ctx
parameter specifies the design time context. The package path for DTContext is oracle.adf.model.adapter.DTContext
.
In the initialize
method, you should perform these tasks:
check if the source node is something that you support
if you support the node, then extract all the information that you need to create a data control instance from the source node. If the information is not sufficient to create a data control instance, you can display some UI in the invokeUI
method to get the user to enter the required information.
For the simple CSV adapter, the initialize
method simply sets some class variables. These class variables are checked later in the invokeUI
method.
This method enables you to display any UI to collect information from the user about the dropped data source. The method has the following signature in the AbstractAdapter
:
The method should return false
if the user cancels the operation in the UI. This means that the data control is not created.
The method should return true
(which is the default implementation) if the UI was run to collect the information.
The simple CSV adapter uses the initialize
method to call extractDataSourceInfo
, which performs the following:
checks that the node right-clicked by the user represents a text file and that the filename has a .csv
extension
gets the filename of the CSV file
sets the mSrcLoc
and mDCName
class variables. mSrcLoc
points to the location of the CSV file, and mDCName
is the name used for the data control. In this case, it is just the name of the CSV file without the .csv
extension.
These variables are used by invokeUI
to instantiate a SampleDCDef
object. The SampleDCDef
object, which is another class you have to implement, is described in Section 31.4, "Implement the Data Control Definition Class".
Example 31-6 shows the invokeUI
method:
This method returns the definition of the data control that was created from information gathered from the dropped source node. The method has the following signature:
The AbstractDefinition
class is the data control definition class that you created. See Section 31.4, "Implement the Data Control Definition Class".
In the simple CSV adapter, the getDefinition
method returns the value of the mDefinition
class variable, which was set in the invokeUI
method. mDefinition
refers to the data control definition class that you created (SampleDCDef
in the case of the simple CSV adapter).
This class needs to provide all the information that the framework needs to instantiate a data control during design time and runtime. This class is responsible for performing these operations:
creating a default constructor. See Section 31.4.4, "Creating a Default Constructor".
collecting metadata from the user about the data source. See Section 31.4.5, "Collecting Metadata from the User".
defining the structure of the output. The structure defines what the user sees when the user expands the data control in the Data Control Palette. The user can then drag elements from the data control entry in the Data Control Palette to a page to create a view component. See Section 31.4.6, "Defining the Structure of the Data Control".
creating an instance of the data control class using that metadata. The data control class is a class that you implement. See Section 31.4.7, "Creating an Instance of the Data Control".
enabling the framework to load the metadata from the DCX file. See Section 31.4.8, "Setting the Metadata for Runtime".
setting a name for your data control. See Section 31.4.9, "Setting the Name for the Data Control".
The data control definition class needs to extend the abstract class oracle.adf.model.adapter.AbstractDefinition
. This class is located in the JDEV_HOME/bc4j/lib/adfm.jar
file.
Example 31-9 is an outline showing the methods you have to implement when you create a data control definition class. The sample is taken from SampleDCDef
, which is the data control definition class for the simple CSV data control adapter.
Example 31-9 Outline for the Data Control Definition Class
import oracle.adf.model.adapter.AbstractDefinition; import org.w3c.dom.Node; import oracle.binding.meta.StructureDefinition; import oracle.binding.DataControl; import java.util.Map; public class SampleDCDef extends AbstractDefinition { // default constructor public SampleDCDef () { // you need a default constructor. // see Section 31.4.4, "Creating a Default Constructor". } public Node getMetadata() { // you need to implement this method. // see Section 31.4.5, "Collecting Metadata from the User". } public StructureDefinition getStructure() { // you need to implement this method. // see Section 31.4.6, "Defining the Structure of the Data Control". } public DataControl createDataControl() { // you need to implement this method. // see Section 31.4.7, "Creating an Instance of the Data Control". } public void loadFromMetadata(Node node, Map params) { // you need to implement this method. // see Section 31.4.8, "Setting the Metadata for Runtime". } public String getDCName() { // you need to implement this method. // see Section 31.4.9, "Setting the Name for the Data Control". } }
Example 31-10 shows the complete source for the SampleDCDef
class:
Example 31-10 Complete Source for the SampleDCDef Class
package oracle.adfinternal.model.adapter.sample; import java.io.InputStream; import java.util.Map; import oracle.binding.DataControl; import oracle.binding.meta.StructureDefinition; import oracle.adf.model.adapter.AbstractDefinition; import oracle.adf.model.adapter.AdapterDCService; import oracle.adf.model.adapter.AdapterException; import oracle.adf.model.adapter.dataformat.AccessorDef; import oracle.adf.model.adapter.dataformat.StructureDef; import oracle.adf.model.adapter.utils.NodeAttributeHelper; import oracle.adf.model.utils.SimpleStringBuffer; import oracle.adfinternal.model.adapter.sample.CSVHandler; import oracle.adfinternal.model.adapter.sample.SampleDataControl; import oracle.adfinternal.model.adapter.url.SmartURL; import oracle.xml.parser.v2.XMLDocument; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class SampleDCDef extends AbstractDefinition { // Name of the root accessor for a definition public static final String RESULT_ACC_NAME = "Result"; // Namespace for the metadata definition. public static final String SAMPLEDC_NS = "http://xmlns.oracle.com/adfm/adapter/sampledc"; // Definition tag as the root public static final String DEFINITION = "Definition"; // Attribute to contain the source URL public static final String SOURCE_LOC = "SourceLocation"; // Name of the data control private String mName = "SampleDC"; // the structure definition private StructureDef mStructDef = null; // URL for this definition. private String mCSVUrl = null; public SampleDCDef() { } public SampleDCDef(String csvURL,String dcName) { mCSVUrl = csvURL; mName = dcName; } public Node getMetadata() { XMLDocument xDoc = new XMLDocument(); Element metadata = xDoc.createElementNS(SAMPLEDC_NS, DEFINITION); metadata.setAttribute(SOURCE_LOC, mCSVUrl.toString()); return metadata; } public StructureDefinition getStructure() { if (mStructDef == null) { // create an empty StructureDefinition mStructDef = new StructureDef(getName()); SmartURL su = new SmartURL(mCSVUrl.toString()); InputStream isData = su.openStream(); CSVHandler csvHandler = new CSVHandler(isData, true, "UTF-8", ",", "\""); // Name of the accessor or the method structure to hold the attributes String opName = new SimpleStringBuffer(50).append(getDCName()) .append("_") .append(RESULT_ACC_NAME) .toString(); StructureDef def = (StructureDef)csvHandler.getStructure(opName, null); // Create the accessor definition AccessorDef accDef = new AccessorDef(RESULT_ACC_NAME, mStructDef, def, true); def.setParentType(StructureDef.TYPE_ACCESSOR); accDef.setBindPath(new SimpleStringBuffer(50) .append(mStructDef.getFullName()) .append(".") .append(AdapterDCService.DC_ROOT_ACC_NAME) .toString()); mStructDef.addAccessor(accDef); } return mStructDef; } public void loadFromMetadata(Node node, Map params) { try { // Get the information from the definition NodeList listChld = node.getChildNodes(); int cnt = listChld.getLength(); Node chld; for (int i = 0; i < cnt; i++) { chld = listChld.item(i); // System.out.println("Tag: " + chld.getNodeName()); if (DEFINITION.equalsIgnoreCase(chld.getNodeName())) { // Load the required attributes NodeAttributeHelper attribs = new NodeAttributeHelper(chld.getAttributes()); mCSVUrl = attribs.getValue(SOURCE_LOC); } } } catch (AdapterException ae) { throw ae; } catch (Exception e) { throw new AdapterException(e); } } public DataControl createDataControl() { SampleDataControl dcDataControl = new SampleDataControl(mCSVUrl); return dcDataControl; } public String getDCName() { return mName; } public String getAdapterType() { return "oracle.adfm.adapter.SampleDataControl"; } }
You need to create a default constructor for the data control definition class. The simple CSV adapter has an empty default constructor:
The default constructor is used only during runtime. It is not used during design time.
Metadata in a data control adapter provides information on the data source. The data control definition class uses the metadata to create a data control. Examples of metadata for the full-featured CSV data control adapter include the URL to the CSV file, the field separator character, and the quote character. For the simple CSV adapter, the metadata consists of only the location of the CSV file.
A data control adapter can collect metadata in different ways. Examples:
The CSV data control adapter that comes with JDeveloper uses a wizard to collect metadata from the user.
The web service data control adapter also uses a wizard to collect metadata. Alternatively, users can drag a web service connection node and drop it on the Data Control Palette. The web service adapter extracts metadata from the node instead of launching the wizard.
When the user drags and drops a node onto the Data Control Palette, the adapter framework looks for an adapter that can handle the type of node that was dropped by searching the registered data control adapters. Data control adapters declare which node types they support. The nodes are JDeveloper nodes that represent specific source types. When the framework finds an adapter that supports the type of node that was dropped, it invokes the data control adapter, which then extracts the required information from the node.
The simple CSV adapter extracts metadata from a node when the user right-clicks a node and selects "Create Data Control" from the context menu.
Regardless of how a data control adapter retrieves the metadata, you must implement the getMetadata
method in your data control definition class. The framework calls the method to get the metadata.
This method returns the metadata in the form of a Node
object. The getMetadata
method has the following signature:
In the simple CSV adapter, the getMetadata
method retrieves the metadata from the mCSVUrl
class variable and inserts the value in an Element
object.
Example 31-13 getMetadata Method
public Node getMetadata() { XMLDocument xDoc = new XMLDocument(); Element metadata = xDoc.createElementNS(SAMPLEDC_NS, DEFINITION); metadata.setAttribute(SOURCE_LOC, mCSVUrl.toString()); return metadata; }
The framework extracts the information from getMetadata
's return value (the Node
object) and writes the information to the DataControls.dcx
file. For example, after the user has created a CSV data control, the file looks like the following:
Example 31-14 DataControls.dcx File
<?xml version="1.0" encoding="UTF-8" ?> <DataControlConfigs xmlns="http://xmlns.oracle.com/adfm/configuration" version="10.1.3.36.45" Package="view" id="DataControls"> <AdapterDataControl id="testdata" FactoryClass="oracle.adf.model.adapter.DataControlFactoryImpl" ImplDef="oracle.adfinternal.model.adapter.sample.SampleDCDef" SupportsTransactions="false" SupportsSortCollection="false" SupportsResetState="false" SupportsRangesize="false" SupportsFindMode="false" SupportsUpdates="false" Definition="testdata" BeanClass="testdata" xmlns="http://xmlns.oracle.com/adfm/datacontrol"> <Source> <Definition SourceLocation="/C:/Application1/ViewController/public_html/testdata.csv"/> </Source> </AdapterDataControl> </DataControlConfigs>
The value of the id
attribute of the AdapterDataControl
tag ("testdata
") is extracted from the name of the CSV file. The other attributes in the AdapterDataControl
tag contain information about the simple CSV adapter itself. In the Definition
element, the framework writes the metadata provided by the node; the SourceLocation
attribute specifies the location of the CSV file.
Structure in a data control definition describes the items that appear when the user expands the data control in the Data Control Palette. Items that can appear include methods, accessors, and attributes of the underlying service that are available to the user to invoke or display. The user can drag these items onto a view page.
In your data control definition class, you need to implement the getStructure
method. The framework calls this method when the user expands the data control in the Data Control Palette.
The getStructure
method has the following signature:
StructureDefinition
is an interface. You can find more information about this interface in the online help in JDeveloper, under Reference > Oracle ADF Model API Reference.
Example 31-16 getStructure Method
public StructureDefinition getStructure() { if (mStructDef == null) { // create an empty StructureDefinition mStructDef = new StructureDef(getName()); SmartURL su = new SmartURL(mCSVUrl.toString()); InputStream isData = su.openStream(); CSVHandler csvHandler = new CSVHandler(isData, true, "UTF-8", ",", "\""); // Name of the accessor or the method structure to hold the attributes String opName = new SimpleStringBuffer(50).append(getDCName()) .append("_") .append(RESULT_ACC_NAME) .toString(); StructureDef def = (StructureDef)csvHandler.getStructure(opName, null); // Create the accessor definition AccessorDef accDef = new AccessorDef(RESULT_ACC_NAME, mStructDef, def, true); def.setParentType(StructureDef.TYPE_ACCESSOR); accDef.setBindPath(new SimpleStringBuffer(50) .append(mStructDef.getFullName()) .append(".") .append(AdapterDCService.DC_ROOT_ACC_NAME) .toString()); mStructDef.addAccessor(accDef); } return mStructDef; }
The framework calls the createDataControl
method in the data control definition class to create a data control instance. The createDataControl
method has the following signature:
The DataControl
object returned by the method is an instance of the data control class that you create. Section 31.5, "Implement the Data Control Class" describes this class.
In the data control definition for the simple CSV adapter, the createDataControl
method looks like the following:
Example 31-18 createDataControl Method
public DataControl createDataControl() { SampleDataControl dcDataControl = new SampleDataControl(mCSVUrl); return dcDataControl; }
The SampleDataControl
class is described in more detail in Section 31.5, "Implement the Data Control Class".
When the user runs the view page that references your data control, the framework reads the metadata from the DCX file and invokes the loadFromMetadata
method in the data control definition class to load the data control with the metadata saved during design time.
Recall that the framework wrote the metadata to the DCX file in the getMetadata
method. See Section 31.4.5, "Collecting Metadata from the User".
The loadFromMetadata
method has the following signature:
Example 31-19 loadFromMetadata Signature
public void loadFromMetadata(org.w3c.dom.Node node, java.util.Map params);
The node
parameter contains the metadata. In the simple CSV adapter, the method looks like the following:
Example 31-20 loadFromMetadata Method
public void loadFromMetadata(Node node, Map params) { try { // Get the information from the definition NodeList listChld = node.getChildNodes(); int cnt = listChld.getLength(); Node chld; for (int i = 0; i < cnt; i++) { chld = listChld.item(i); // System.out.println("Tag: " + chld.getNodeName()); if (DEFINITION.equalsIgnoreCase(chld.getNodeName())) { // Load the required attributes NodeAttributeHelper attribs = new NodeAttributeHelper(chld.getAttributes()); mCSVUrl = attribs.getValue(SOURCE_LOC); } } } catch (AdapterException ae) { throw ae; } catch (Exception e) { throw new AdapterException(e); } }
You need to implement the getDCName
method to return a string that is used to identify the data control instance in the Data Control Palette. getDCName
has the following signature:
In the simple CSV adapter, the method just returns the value of the mName
class variable, which was set by the SampleDCDef(String csvURL, String dcName)
constructor. This constructor was called in the SampleDCAdapter
class. mName
is the name of the CSV file without the .csv
extension.
Note that each data control instance must have a unique name within an application. For example, if you have two CSV data controls in an application, you can name them "CSV1" and "CSV2". For the CSV data control adapter that is shipped with JDeveloper, the user can enter the name in the wizard. For the simple CSV adapter, the name is the name of the CSV file without the .csv
extension.
The data control class must be able to access the data source based on the metadata that was saved during design time. This class is instantiated by the createDataControl
method in the data control definition class (see Section 31.4.7, "Creating an Instance of the Data Control").
This class needs to:
Extend the abstract class oracle.adf.model.AbstractImpl
.
Implement one of the following data control interfaces:
Table 31-1 Data Control Interfaces
Interface | When to Use |
---|---|
|
Implement this interface if you do not need to demarcate the start and end of a request and if you do not need transactional support. |
|
Implement this interface if you need to demarcate the start and end of a request. This interface extends |
|
Implement this interface if you need transactional support. The interface requires you to implement the |
The abstract class oracle.adf.model.AbstractImpl
is located in the JDEV_HOME/bc4j/lib/adfm.jar
file.
The data control interfaces are located in the JDEV_HOME/bc4j/lib/adfbinding.jar
file.
The following class outline for a data control class shows the methods you have to implement:
Example 31-23 Outline for a Data Control Class
import oracle.adf.model.adapter.AbstractImpl; import oracle.binding.DataControl; import java.util.HashMap; public class SampleDataControl extends AbstractImpl implements ManagedDataControl { public boolean invokeOperation(java.util.Map map, oracle.binding.OperationBinding action) { // you need to implement this method. // see Section 31.5.4, "Implementing the invokeOperation Method". } public String getName() { // you need to implement this method. // see Section 31.5.5, "Implementing the getName Method". } public void release(int flags) { // you need to implement this method. // see Section 31.5.6, "Implementing the release Method". } public Object getDataProvider() { // you need to implement this method. // see Section 31.5.7, "Implementing the getDataProvider Method". } }
Example 31-24 shows the complete source for the SampleDataControl
class.
Example 31-24 Complete Source for the SampleDataControl Class
package oracle.adfinternal.model.adapter.sample; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import javax.naming.Context; import oracle.binding.ManagedDataControl; import oracle.binding.OperationInfo; import oracle.adf.model.adapter.AdapterException; import oracle.adf.model.adapter.AbstractImpl; import oracle.adf.model.adapter.dataformat.CSVHandler; import oracle.adfinternal.model.adapter.url.SmartURL; // Data control that represents a URL data source with CSV data format. public class SampleDataControl extends AbstractImpl implements ManagedDataControl { //URL to access the data source private String mCSVUrl = null; public SampleDataControl() { } public SampleDataControl(String csvUrl) { mCSVUrl = csvUrl; } public boolean invokeOperation(java.util.Map map, oracle.binding.OperationBinding action) { Context ctx = null; try { // We are interested of method action binding only. if (action == null) { return false; } OperationInfo method = action.getOperationInfo(); // No method defined, we are not interested. if (method == null) { return false; } // Execute only when the adapter execute is invoked if (METHOD_EXECUTE.equals(method.getOperationName())) { Object retVal = null; if (mCSVUrl != null) { SmartURL su = new SmartURL(mCSVUrl); InputStream isData = su.openStream(); CSVHandler csvHandler = new CSVHandler(isData,true,"UTF-8",",","\""); Map properties = new HashMap(); retVal = csvHandler.getResult(properties); } Map rootDataRow = new java.util.HashMap(2); rootDataRow.put(SampleDCDef.RESULT_ACC_NAME, retVal); ArrayList aRes = new ArrayList(2); aRes.add(rootDataRow); processResult(aRes.iterator(), map, action); return true; } } catch (AdapterException ae) { throw ae; } catch (Exception e) { throw new AdapterException(e); } return false; } /** * Perform request level initialization of the DataControl. * @param requestCtx a HashMap representing request context. */ public void beginRequest(HashMap requestCtx) { } /** * perform request level cleanup of the DataControl. * @param requestCtx a HashMap representing request context. */ public void endRequest(HashMap requestCtx) { } /** * return false as resetState was deferred to endRequest processing */ public boolean resetState() { return false; } /** * returns the name of the data control. */ public String getName() { return mName; } /** * releases all references to the objects in the data provider layer */ public void release(int flags) { } /** * Return the Business Service Object that this datacontrol is associated with. */ public Object getDataProvider() { return null; } }
You must implement the invokeOperation
method in your data control class. The framework invokes this method when the user runs the view page.
This method is declared in the DataControl
interface. The method has the following signature:
Example 31-25 invokeOperation Signature
public boolean invokeOperation(java.util.Map bindingContext, oracle.binding.OperationBinding action);
The bindingContext
parameter contains the return values fetched from the data source. The keys for retrieving the values are generated by the framework. Typically you do not need to process the values unless you need to filter or transform them.
The action
parameter specifies the method that generated the values. The method could be a method supported by the underlying service, as in the case of a web service. The framework calls the data control even for some built-in actions if the data control wants to override the default behavior. You can check this parameter to determine if you need to process the action or not. For data controls that represent data sources that do not expose methods, the framework creates an action AbstractImpl.METHOD_EXECUTE
to the execute the query for a data control.
The method should return false
if it does not handle an action.
In the simple CSV adapter, the invokeOperation
method checks that the method is METHOD_EXECUTE before fetching the data. It invokes the CSVHandler
class, which invokes the CSVParser
class, to get the data from the CSV file.
Example 31-26 invokeOperation Method
public boolean invokeOperation(java.util.Map map, oracle.binding.OperationBinding action) { Context ctx = null; try { // We are interested in method action binding only. if (action == null) { return false; } OperationInfo method = action.getOperationInfo(); // No method defined, we are not interested. if (method == null) { return false; } // Execute only when the adapter execute is invoked if (METHOD_EXECUTE.equals(method.getOperationName())) { Object retVal = null; if (mCSVUrl != null) { SmartURL su = new SmartURL(mCSVUrl); InputStream isData = su.openStream(); CSVHandler csvHandler = new CSVHandler(isData, true, "UTF-8", ",", "\""); Map properties = new HashMap(); retVal = csvHandler.getResult(properties); } Map rootDataRow = new java.util.HashMap(2); rootDataRow.put(SampleDCDef.RESULT_ACC_NAME, retVal); ArrayList aRes = new ArrayList(2); aRes.add(rootDataRow); processResult(aRes.iterator(), map, action); return true; } } catch (AdapterException ae) { throw ae; } catch (Exception e) { throw new AdapterException(e); } return false; }
Note that invokeOperation
calls the processResult
method after fetching the data. See the next section for details.
invokeOperation
should call processResult
to provide updated values to the framework. The method puts the result into the binding context for the framework to pick up. The method has the following syntax:
Example 31-27 processResult Syntax
public void processResult(Object result, Map bindingContext, oracle.binding.OperationBinding action)
In the result
parameter, specify the updated values.
In the bindingContext
parameter, specify the binding context. This is typically the same binding context passed into the invokeOperation
method.
In the action
parameter, specify the operation. This is typically the same action value passed into the invokeOperation
method.
Implement the getName
method to return the name of the data control as used in a binding context.
This method is declared in the DataControl
interface. It has the following signature:
In the simple CSV adapter, the method simply returns mName
, which is a variable defined in the AbstractImpl
class.
The framework calls the release method to release all references to the objects in the data provide layer.
This method is declared in the DataControl
interface. It has the following signature:
The flags
parameter indicate which references should be released:
REL_ALL_REFS
: The data control should release all references to the view and model objects.
REL_DATA_REFS
: The data control should release references to data provider objects.
REL_VIEW_REFS
: The data control should release all references to view or UI layer objects.
In the simple CSV data control adapter, the release
method is empty. However, if your data control uses a connection, it should close and release the connection in this method.
This method returns the business service object associated with this data control.
This method is declared in the DataControl
interface. It has the following signature:
In the simple CSV data control adapter, this method just returns null.
In addition to the required classes, which implement ADF interfaces, you can create any supporting classes for your adapter, if necessary. The simple CSV adapter includes two supporting classes: CSVHandler
and CSVParser
. These classes read and parse the CSV files into rows and fields. See Section 31.11, "Contents of Supporting Files" for complete source listing for these classes.
To define your adapter for JDeveloper, create a file called adapter-definition.xml
and place it in a directory called meta-inf
. Note that the file and directory names are case-sensitive.
A typical adapter-definition.xml
file contains the following entries:
Example 31-32 Description of adapter-definition.xml File
<AdapterDefinition> <Adapter Name="unique name for the adapter" ClassName="full name of class that implements AbstractAdapter"> <Schema Namespace="name of schema that defines the data control metadata for this adapter" Location="location of schema definition file"/> <Source> <Type Name="name of source type that the adapter can handle to create a data control" JDevNode="full class name of supported node"/> </Source> <JDevContextHook Class="full name of class that provides the JDeveloper context hook, if any"/> <Dependencies> <Library Path="full path name of the JAR file that the adapter needs in order to run"/> </Dependencies> </Adapter> </AdapterDefinition>
The AdapterDefinition
tag is the container tag for all adapters.
Each Adapter
tag describes an adapter. It has the following attributes:
Name
specifies a unique name for the adapter. The framework uses this name to identify the adapter.
ClassName
specifies the full Java class that implements the AbstractAdapter
.
The Schema
tag defines the namespace and the schema definition for the adapter metadata. JDeveloper registers the schema so that the metadata can be validated at design time. You can define all the namespaces and schemas supported by the adapters. This is optional.
The Source
tag specifies the node (or data source) types that the adapter supports. It has the following attributes:
JDevNode
specifies the Java class for the supported node type. This node type can appear in JDeveloper's Connection Navigator.
Name: any string
The JDevContextHook
tag specifies additions to the context menu (the menu that appears when the user right clicks on the metadata node for the data control instance in the Structure Pane).
The Dependencies
tag lists the library files that your adapter requires during runtime. The framework adds the library files to the project when the user uses a data control based on your adapter.
The adapter-definition.xml
file for the simple CSV data control adapter looks like the following:
Example 31-33 adapter-definition.xml File for the Simple CSV Adapter
<AdapterDefinition> <Adapter Name="oracle.adfm.adapter.SampleDataControl" ClassName="oracle.adfinternal.model.adapter.sample.SampleDCAdapter"> <Schema Namespace="http://xmlns.oracle.com/adfm/adapter/sample" Location="/oracle/adfinternal/model/adapter/sample/sampleDC.xsd"/> <Source> <Type Name="csvNode" JDevNode="oracle.ide.model.TextNode"/> </Source> <Dependencies> <Library Path="${oracle.home}/jlib/sampledc.jar"/> </Dependencies> </Adapter> </AdapterDefinition>
The sampleDC.xsd
file is shown in Section 31.11.1, "sampleDC.xsd".
You need to add the following libraries to your project in order to build your adapter:
In the Project Properties dialog in JDeveloper, select Libraries on the left side.
Click Add Library on the right side and add the following libraries:
JSR-227 API
ADF Model Generic Runtime
Oracle XML Parser v2
Click Add Jar/Directory on the right side and add the following libraries:
JDEV_HOME/ide/lib/ide.jar
JDEV_HOME/ide/lib/javatools.jar
JDEV_HOME/bc4j/jlib/dc-adapter.jar
Perform these steps to deploy your adapter to JDeveloper:
Create an extension.xml
file in the meta-inf
directory (the same directory that contains the adapter-definition.xml
file).
You need to do this because you are deploying the adapter as a JDeveloper extension. You use the extension.xml
to add your JAR files to JDeveloper's classpath.
The extension.xml
file contains the following lines:
Example 31-34 extension.xml
<?xml version = '1.0' encoding = 'UTF-8'?> <extension xmlns="http://jcp.org/jsr/198/extension-manifest" id="oracle.adfm.sample-adapters" version="10.1.3.36.45" esdk-version="1.0"> <name>ADFm Sample Adapter</name> <owner>Oracle Corporation</owner> <dependencies> <import>oracle.BC4J</import> <import>oracle.j2ee</import> </dependencies> <classpaths> <classpath>../../BC4J/jlib/dc-adapters.jar</classpath> <classpath>../../jlib/sampledc.jar</classpath> </classpaths> <hooks> <!-- Adapter-specific data control library definitions --> <libraries xmlns="http://xmlns.oracle.com/jdeveloper/1013/jdev-libraries"> <library name="Sample Data Control" deployed="true"> <classpath>../../jlib/sampledc.jar</classpath> </library> </libraries> </hooks> </extension>
For details on the tags in the extension.xml
file, see the file JDEV_HOME/jdev/doc/extension/ide-extension-packaging.html
.
Create a JAR file that contains the class files for your adapter, the adapter-definition.xml
file, and the extension.xml
file. The XML files must be in a meta-inf
directory.
For the simple CSV adapter, the JAR file is called sampledc.jar, and it contains the following files:
Example 31-35 sampledc.jar
connections.xml extension/meta-inf/extension.xml meta-inf/adapter-definition.xml meta-inf/Manifest.mf oracle/adfinternal/model/adapter/sample/CSVHandler$1.class oracle/adfinternal/model/adapter/sample/CSVHandler.class oracle/adfinternal/model/adapter/sample/CSVParser.class oracle/adfinternal/model/adapter/sample/SampleDataControl.class oracle/adfinternal/model/adapter/sample/SampleDCAdapter.class oracle/adfinternal/model/adapter/sample/SampleDCDef.class
Copy the JAR file to the JDEV_HOME/jlib
directory.
Create another JAR file to contain only the extension.xml file and the manifest file in the meta-inf directory. For the simple CSV adapter, the JAR file is called oracle.adfm.sampledc.10.1.3.jar, and it contains the following files:
Copy the second JAR file (for example, oracle.adfm.sampledc.10.1.3.jar
) to the JDEV_HOME/jdev/extensions
directory.
Stop JDeveloper, if it is running.
Start JDeveloper. When you right-click on a node type that your adapter supports, you should see the "Create Data Control" menu item.
If you want more information on JDeveloper extensions, you can download the Extension SDK:
In JDeveloper, choose Help | Check for Updates. This starts the Check for Updates wizard.
On the Welcome page of the wizard, click Next.
On the Source page, select Search Update Centers, and select all the locations listed in that section. Click Next.
On the Updates page, select Extension SDK. Click Next to download and install the extension SDK.
On the Summary page, click Finish. You will need to restart JDeveloper so that it can access the Extension SDK files.
For help on the Extension SDK, open the JDeveloper's online help, and navigate to Extending JDeveloper > Extending JDeveloper with the Extension SDK.
The JDeveloper online help provides reference information for the classes described in this chapter in Javadoc format.
Table 31-2 Location of Javadoc
Class / Interface | Location of Javadoc in the Online Help |
---|---|
|
Reference > Oracle ADF Model API Reference > Packages > oracle.adf.model.adapter > Class Summary |
|
Reference > Oracle ADF Model API Reference > Packages > oracle.binding.meta > Interface Summary |
|
Reference > Oracle ADF Model API Reference > Packages > oracle.binding > Interface Summary |
This section shows the contents of the following files:
Example 31-37 shows the contents of the sampleDC.xsd
file.
Example 31-37 sampleDC.xsd
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://xmlns.oracle.com/adfm/adapter/test" xmlns="http://xmlns.oracle.com/adfm/adapter/test" elementFormDefault="qualified"> <xsd:element name="Definition"> <xsd:complexType> <xsd:attribute name="SourceLocation" type="xsd:string"/> </xsd:complexType> </xsd:element> </xsd:schema>
Example 31-38 shows the contents of the CSVHandler
class.
Example 31-38 CSVHandler
package oracle.adfinternal.model.adapter.sample; import java.io.InputStream; import java.util.Iterator; import java.util.List; import java.util.Map; import oracle.binding.meta.DefinitionContext; import oracle.binding.meta.StructureDefinition; import oracle.adf.model.utils.SimpleStringBuffer; import oracle.adf.model.adapter.AdapterException; import oracle.adf.model.adapter.dataformat.AttributeDef; import oracle.adf.model.adapter.dataformat.StructureDef; import oracle.adfinternal.model.adapter.sample.CSVParser; import oracle.adf.model.adapter.utils.Utility; /** * Format handler for character separated values. * <p> * This class generates structures according to the JSR 227 specification from * a CSV data stream by parsing the data. The data types are guessed from the * value of the first data line. It can extract values from a CSV data stream * as well. * <p> * Data controls that deals with CSV data can use this class to generate data * and structure. * * @version 1.0 * @since 10.1.3 */ public class CSVHandler { // stream containing the data. private InputStream mDataStream; // if the first row contains the names private boolean mIsFirstRowNames = false; // Encoding styles private String mEncStyle; // Character value separator private String mDelimiter; // Character used to quote a multi-word string private String mQuoteChar; // Column names private List mColNames = null; ////////////////////////////// Constructors ////////////////////////////////// /** * Creats a CSV format handler object. * * @param is input stream that contains the CSV data. * @param isFirstRowNames flag to indicate if the first row of the CSV data * can be treated as column names. * @param encodingStyle encoding style of the data. * @param delim character value separators. * @param quoteChar value that can be treated as quote. */ public CSVHandler( InputStream is, boolean isFirstRowNames, String encodingStyle, String delim, String quoteChar) { mDataStream = is; mIsFirstRowNames = isFirstRowNames; mEncStyle = encodingStyle; mDelimiter = delim; mQuoteChar = quoteChar; } ///////////////////// Impl of FormatHandler ////////////////////////////////// /** * Returns the structure definition extracted for the data format. * <p> * * @param name name of the root structure. * @param ctx definition context information. * @return the structure information extracted. */ public StructureDefinition getStructure(String name, DefinitionContext ctx) { StructureDef attrParent = null; try { CSVParser parser; if (mEncStyle == null) { parser = new CSVParser(mDataStream); } else { parser = new CSVParser(mDataStream, mEncStyle); } parser.setSeparators(mDelimiter.toCharArray()); if (mQuoteChar != null && mQuoteChar.length() != 0) { parser.setQuoteChar(mQuoteChar.charAt(0)); } // Get the column names Iterator colNames = getColNames(parser).iterator(); // Create the structure definition attrParent = new StructureDef(name); // Parse the data to get the attributes if (mIsFirstRowNames) { parser.nextLine(); } String[] vals = parser.getLineValues(); if (vals != null) { int i = 0; while (colNames.hasNext()) { String type = "java.lang.String"; if (i < vals.length) { type = checkType(vals[i]); ++i; } AttributeDef attr = new AttributeDef((String) colNames.next(), attrParent, type); attrParent.addAttribute(attr); } } else { while (colNames.hasNext()) { AttributeDef attr = new AttributeDef((String) colNames.next(), attrParent, "java.lang.String"); attrParent.addAttribute(attr); } } } catch (Exception e) { throw new AdapterException(e); } return attrParent; } /** * Returns the resulting data extracted from the input. * @param params parameters passed containig the context information. * @return <code>Iterator</code> of <code>Map</code> objects for the result. * If no data found it can return null. The <code>Map</code> * contains the value of attributes as defined in the data structure. * For complex data, <code>Map</code>s can contain other iterator of * <code>Map</code>s as well. */ public Iterator getResult(Map params) { try { final CSVParser parser; if (mEncStyle == null) { parser = new CSVParser(mDataStream); } else { parser = new CSVParser(mDataStream, mEncStyle); } parser.setSeparators(mDelimiter.toCharArray()); if (mQuoteChar != null && mQuoteChar.length() != 0) { parser.setQuoteChar(mQuoteChar.charAt(0)); } final List cols = getColNames(parser); final boolean bEndOfData = (mIsFirstRowNames) ? !parser.nextLine() : false; //return the data iterator return new Iterator() { CSVParser _parser = parser; Iterator _colNames = cols.iterator(); boolean _eof = bEndOfData; public void remove() { } public boolean hasNext() { return !_eof; } public Object next() { try { if (_eof) { return null; } java.util.HashMap map = new java.util.HashMap(5); // Create the current row as Map String[] data = _parser.getLineValues(); int i = 0; while (_colNames.hasNext()) { String val = null; if (i < data.length) { val = data[i]; } map.put(_colNames.next(), val); i++; } // get the next data line. _eof = !_parser.nextLine(); return map; } catch (Exception e) { throw new AdapterException(e); } } }; } catch (AdapterException ae) { throw ae; } catch (Exception e) { throw new AdapterException(e); } } //============================================================================ // Class Helper Methods //============================================================================ /** * Attempts to obtain the Java type from the string value. * @param data String value whose datatype has to be guessed. * @return Java type name. */ private String checkType(String data) { try { // We first try to convert the value into a long number. // If successful, we will use long; if it throws NumberFormatException, // we will attempt to convert it to float. If this too fails, we return // string. if (data != null) { try { // Try to conver the value into an integer number. long numTest = Long.parseLong(data); return "java.lang.Long"; //NOTRANS } catch (NumberFormatException nfe) { // Try to convert the value into float number. float numTest = Float.parseFloat(data); return "java.lang.Float"; //NOTRANS } } else { return "java.lang.String"; //NOTRANS } } catch (NumberFormatException nfe) { // If conversion failed, we assume this is a string. return "java.lang.String"; } } /** * Gets the column names. */ /** * Gets the column names. */ private List getColNames(CSVParser parser) { try { if (mColNames == null) { // Get the first row. If the first row is NOT the column names, we need // to generate column names for them. if (!parser.nextLine()) { // No data found. // ToDo: resource new Exception("No data"); } mColNames = new java.util.ArrayList(10); String[] cols = parser.getLineValues(); if (mIsFirstRowNames) { makeValidColumnNames(cols); for (int i = 0; i < cols.length; i++) { mColNames.add(cols[i]); } } else { for (int i = 0; i < cols.length; i++) { String colName = new SimpleStringBuffer(20).append("Column").append(i).toString(); mColNames.add(colName); } } } return mColNames; } catch (Exception e) { throw new AdapterException(e); } } /** * Make valid column names for all columns in CSV data source. * * This method applies the following rules to translate the given string * to a valid column name which can be accepted by EL: * * 1. If the first character of the string is digit, * prefix the string with '_'. * 2. Translate any characters other than letter, digit, or '_' to '_'. * * */ private String[] makeValidColumnNames(String[] cols) { for (int i = 0; i <cols.length; i++) { // Trim out leading or ending white spaces if (cols[i] != null && cols[i].length() > 0) { cols[i] = cols[i].trim(); } if (cols[i] == null || cols[i].length() == 0) { // Default as "column1", "column2", ... if column name null cols[i] = new SimpleStringBuffer("column").append(i+1).toString(); } else { // Check special characters try { cols[i] = Utility.normalizeString(cols[i]); } catch (Exception e) { // On error, simply default to "columnX". cols[i] = new SimpleStringBuffer("column").append(i+1).toString(); } } } return cols; } }
Example 31-39 shows the contents of the CSVParser
class.
Example 31-39 CSVParser
package oracle.adfinternal.model.adapter.sample; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.util.ArrayList; import oracle.adf.model.utils.SimpleStringBuffer; public final class CSVParser { /////////////////////////////// Constants //////////////////////////////////// /** UTF8 encoding, used for hadling data in different languages. */ public static final String UTF8_ENCODING = "UTF8"; /** Quote character */ private static char CHAR_QUOTE = '"'; /** Comma (seperator) character */ private static char CHAR_COMMA = ','; /////////////////////////////// Class Variables ////////////////////////////// /** * CSV stream reader */ private LineNumberReader mReader; /** Buffer to store one line of values. */ private ArrayList mValueArrayList = new ArrayList(); /** Buffer to store one string value. */ private SimpleStringBuffer mValueBuffer = new SimpleStringBuffer(256); /** Current processed line. */ private String mLine = null; /** Current character position in the current line. */ private int mLinePosition = -1; /** Length of current line. */ private int mLineLength = 0; /** If last character is comma. */ private boolean mLastCharIsComma = false; /** Value separator character set. The separator can be one of these values.*/ private char[] mSepCharSet = {CHAR_COMMA}; /** Quote character. */ private char mQuoteChar = CHAR_QUOTE; ////////////////////////////// Constructors ////////////////////////////////// /** * Constructor * * @param pInputStream CSV input stream * @throws Exception any error occurred */ public CSVParser(InputStream pInputStream) throws Exception { // If no encoding is passed in, use "UTF-8" encoding this(pInputStream, UTF8_ENCODING); } /** * Constructor * * @param pInputStream CSV input stream * @param pEnc character encoding * @throws Exception any error occurred */ public CSVParser(InputStream pInputStream, String pEnc) throws Exception { if (pInputStream == null) { throw new Exception("Null Input Stream."); //TODO: Resource } mReader = new LineNumberReader(new InputStreamReader(pInputStream, pEnc)); } ///////////////////////////// Public Methods ///////////////////////////////// /** * Sets the separator characters as a list of possible separators for the * data. CSV data may have more than one separators. By default this parser * considers comma (,) as the data separator. * @param seps Array of separator charactors. */ public void setSeparators(char[] seps) { if ((seps != null) && (seps.length > 0)) { mSepCharSet = seps; } } /** * Sets the quote character. * @param ch Quote character. */ public void setQuoteChar(char ch) { mQuoteChar = ch; } /** * Moves to the next line of the data. * @return returns false if the end of data reached. * @throws Exception any error occurred */ public boolean nextLine() throws Exception { setLine(mReader.readLine()); if (mLine == null) { // End of file mValueArrayList.clear(); return false; } parseLine(); return true; } /** * Gets values of next line. * @return next line elements from input stream. If end of data reached, * it returns null. * @throws Exception any error occurred */ public String[] getLineValues() throws Exception { if (mValueArrayList.size() > 0) { String[] ret = new String[mValueArrayList.size()]; return (String[]) mValueArrayList.toArray(ret); } return null; } //////////////////////////// Class Helpers /////////////////////////////////// /** * Checks if the character is a valid separator. */ private boolean isSeparator(char ch) { for (int i = 0; i < mSepCharSet.length; i++) { if (ch == mSepCharSet[i]) { return true; } } return false; } /** * Tests if end of line has reached. * @return true if end of line. */ public boolean isEndOfLine() { // If last char is comma, must return at least one more value return (mLinePosition >= mLineLength) && (!mLastCharIsComma); } /** * Sets current line to be processed * * @param line the line to be processed */ private void setLine(String line) { mLine = line; if (line != null) { mLineLength = line.length(); mLinePosition = 0; } } /** * If next character is quote character * * @return true if next character is quote */ private boolean isNextCharQuote() { if ((mLinePosition + 1) >= mLineLength) { // no more char in the line return false; } else { char ch = mLine.charAt(mLinePosition + 1); if (ch == mQuoteChar) { return true; } else { return false; } } } /** * Parse one line. * * @return values of the line * @throws Exception any error occurred */ private void parseLine() throws Exception { mValueArrayList.clear(); String[] values = null; String value = null; while (!isEndOfLine()) { value = getNextValue(); mValueArrayList.add(value); } } /** * Gets next value from current line. * @return next data value. */ private String getNextValue() throws Exception { mLastCharIsComma = false; // Clean up value buffer first if (mValueBuffer.length() > 0) { mValueBuffer.setLength(0); } boolean insideQuote = false; boolean firstChar = true; boolean endValue = false; // Scan char by char while ((mLinePosition < mLineLength) && !endValue) { boolean copyChar = true; char ch = mLine.charAt(mLinePosition); // If first char if (firstChar) { // Only check quote at first char if (ch == mQuoteChar) { insideQuote = true; copyChar = false; } // Also need to check comma at first char else if (isSeparator(ch)) { copyChar = false; endValue = true; mLastCharIsComma = true; } firstChar = false; } // Not first char but inside quote else if (insideQuote) { // Check end quote if (ch == mQuoteChar) { copyChar = false; // Two sucesstive quote chars inside quote means quote char itself if (isNextCharQuote()) { mLinePosition++; } // Otherwise it is ending quote else { insideQuote= false; } } } // Not first char and outside quote else { // Check comma if (isSeparator(ch)) { copyChar = false; endValue = true; mLastCharIsComma = true; } } if (copyChar) { mValueBuffer.append(ch); } mLinePosition++; } if (mValueBuffer.length() > 0) { return mValueBuffer.toString(); } else { return null; } } }