Skip Headers
Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3.0)

Part Number B28967-02
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

20 Creating Data Control Adapters

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:

This chapter describes the second option: creating a data control adapter. For information about data controls, see .

This chapter contains the following topics:

20.1 Introduction to the Simple CSV Data Control Adapter

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:

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 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:

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.

20.2 Overview of Steps to Create a Data Control Adapter

To create data control adapters:

  1. 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 20.6, "Create any Necessary Supporting Classes".

  2. 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 20.7, "Create an XML File to Define Your Adapter".

  3. 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 20.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 20.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.

20.3 Implement the Abstract Adapter Class

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 20.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:

20.3.1 Location of JAR Files

The abstract class oracle.adf.model.adapter.AbstractAdapter is located in the JDEV_HOME/bc4j/lib/adfm.jar file.

20.3.2 Abstract Adapter Class Outline

Example 20-1 shows an outline of a class that implements the AbstractAdapter class.

Example 20-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 20.3.4, "Implementing the initialize Method".
   }

   public boolean invokeUI()
   {
      // you need to implement this method.
      // see Section 20.3.5, "Implementing the invokeUI Method".
   }

   public AbstractDefinition getDefinition()
   {
      // you need to implement this method.
      // see Section 20.3.6, "Implementing the getDefinition Method".
   }
}

20.3.3 Complete Source for the SampleDCAdapter Class

Example 20-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 20-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;
   }

}

20.3.4 Implementing the initialize Method

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:

Example 20-3 initialize Signature

public abstract void initialize(Object sourceObj, DTContext ctx);

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.

Example 20-4 initialize Method

public void initialize(Object sourceObj, DTContext ctx)
   {
      mSrc = sourceObj;
      mJdevCtx = (Context) ctx.get(DTContext.JDEV_CONTEXT);
   }

20.3.5 Implementing 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:

Example 20-5 invokeUI Signature

public boolean invokeUI()
{
   return true;
}

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 20.4, "Implement the Data Control Definition Class".

Example 20-6 shows the invokeUI method:

Example 20-6 invokeUI

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;
   }

20.3.6 Implementing the getDefinition 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:

Example 20-7 getDefinition Signature

public abstract AbstractDefinition getDefinition();

The AbstractDefinition class is the data control definition class that you created. See Section 20.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).

Example 20-8 getDefinition

public AbstractDefinition getDefinition()
   {
      return mDefinition;
   }

20.4 Implement the Data Control Definition Class

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:

20.4.1 Location of JAR Files

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.

20.4.2 Data Control Definition Class Outline

Example 20-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 20-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 20.4.4, "Creating a Default Constructor".
   }

   public Node getMetadata()
   {
      // you need to implement this method.
      // see Section 20.4.5, "Collecting Metadata from the User".
   }
   public StructureDefinition getStructure()
   {
      // you need to implement this method.
      // see Section 20.4.6, "Defining the Structure of the Data Control".
   }

   public DataControl createDataControl()
   {
      // you need to implement this method.
      // see Section 20.4.7, "Creating an Instance of the Data Control".
   }

   public void loadFromMetadata(Node node, Map params)
   {
      // you need to implement this method.
      // see Section 20.4.8, "Setting the Metadata for Runtime".
   }

   public String getDCName()
   {
      // you need to implement this method.
      // see Section 20.4.9, "Setting the Name for the Data Control".
   }
}

20.4.3 Complete Source for the SampleDCDef Class

Example 20-10 shows the complete source for the SampleDCDef class:

Example 20-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";
   }
}

20.4.4 Creating a Default Constructor

You need to create a default constructor for the data control definition class. The simple CSV adapter has an empty default constructor:

Example 20-11 SampleDCDef Default Constructor

public SampleDCDef()
{
}

The default constructor is used only during runtime. It is not used during design time.

20.4.5 Collecting Metadata from the User

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:

Example 20-12 getMetadata Signature

public org.w3c.dom.Node getMetadata();

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 20-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 20-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.

20.4.6 Defining the Structure of the Data Control

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:

Example 20-15 getStructure Signature

public oracle.binding.meta.StructureDefinition getStructure();

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 20-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;
}

20.4.7 Creating an Instance of the Data Control

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:

Example 20-17 createDataControl Signature

public oracle.binding.DataControl createDataControl();

The DataControl object returned by the method is an instance of the data control class that you create. Section 20.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 20-18 createDataControl Method

public DataControl createDataControl()
{
   SampleDataControl dcDataControl = new SampleDataControl(mCSVUrl);
   return dcDataControl;
}

The SampleDataControl class is described in more detail in Section 20.5, "Implement the Data Control Class".

20.4.8 Setting the Metadata for Runtime

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 20.4.5, "Collecting Metadata from the User".

The loadFromMetadata method has the following signature:

Example 20-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 20-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);
   }
}

20.4.9 Setting the Name for the Data Control

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:

Example 20-21 getDCName Signature

public String getDCName();

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.

Example 20-22 getDCName Method

public String getDCName()
{
   return mName;
}

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.

20.5 Implement the Data Control Class

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 20.4.7, "Creating an Instance of the Data Control").

This class needs to:

Table 20-1 Data Control Interfaces

Interface When to Use

oracle.binding.DataControl

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.

oracle.binding.ManagedDataControl

Implement this interface if you need to demarcate the start and end of a request. This interface extends DataControl, which means that you have to implement the methods in DataControl as well.

oracle.binding.TransactionalDataControl

Implement this interface if you need transactional support. The interface requires you to implement the rollbackTransaction and commitTransaction methods, in addition to the methods in the DataControl interface. (TransactionalDataControl extends the DataControl interface.)


20.5.1 Location of JAR Files

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.

20.5.2 Data Control Class Outline

The following class outline for a data control class shows the methods you have to implement:

Example 20-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 20.5.4, "Implementing the invokeOperation Method".
   }

   public String getName()
   {
      // you need to implement this method.
      // see Section 20.5.5, "Implementing the getName Method".
   }

   public void release(int flags)
   {
      // you need to implement this method.
      // see Section 20.5.6, "Implementing the release Method".
   }

   public Object getDataProvider()
   {
      // you need to implement this method.
      // see Section 20.5.7, "Implementing the getDataProvider Method".
   }
}

20.5.3 Complete Source for the SampleDataControl Class

Example 20-24 shows the complete source for the SampleDataControl class.

Example 20-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;
   }

}

20.5.4 Implementing the invokeOperation Method

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 20-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 20-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.

20.5.4.1 About Calling processResult

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 20-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.

20.5.4.2 Return Value for invokeOperation

Return true from invokeOperation if you handled the action in the method. Return false if the action should be handled by the framework.

20.5.5 Implementing the getName 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:

Example 20-28 getName Signature

public String getName();

In the simple CSV adapter, the method simply returns mName, which is a variable defined in the AbstractImpl class.

Example 20-29 getName Method

public String getName()
{
   return mName;
}

20.5.6 Implementing the release Method

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:

Example 20-30 release Signature

public void release(int flags);

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.

20.5.7 Implementing the getDataProvider 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:

Example 20-31 getDataProvider Signature

public Object getDataProvider();

In the simple CSV data control adapter, this method just returns null.

20.6 Create any Necessary Supporting Classes

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 20.11, "Contents of Supporting Files" for complete source listing for these classes.

20.7 Create an XML File to Define Your Adapter

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 20-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:

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:

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 20-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 20.11.1, "sampleDC.xsd".

20.8 Build Your Adapter

You need to add the following libraries to your project in order to build your adapter:

  1. In the Project Properties dialog in JDeveloper, select Libraries on the left side.

  2. Click Add Library on the right side and add the following libraries:

    • JSR-227 API

    • ADF Model Generic Runtime

    • Oracle XML Parser v2

  3. 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

20.9 Package and Deploy Your Adapter to JDeveloper

Perform these steps to deploy your adapter to JDeveloper:

  1. 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 20-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.

  2. 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 20-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
    
  3. Copy the JAR file to the JDEV_HOME/jlib directory.

  4. 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:

    Example 20-36 oracle.adfm.sampledc.10.1.3.jar

    meta-inf/extension.xml
    meta-inf/Manifest.mf
    
  5. Copy the second JAR file (for example, oracle.adfm.sampledc.10.1.3.jar) to the JDEV_HOME/jdev/extensions directory.

  6. Stop JDeveloper, if it is running.

  7. 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:

  1. In JDeveloper, choose Help | Check for Updates. This starts the Check for Updates wizard.

  2. On the Welcome page of the wizard, click Next.

  3. On the Source page, select Search Update Centers, and select all the locations listed in that section. Click Next.

  4. On the Updates page, select Extension SDK. Click Next to download and install the extension SDK.

  5. 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.

20.10 Location of Javadoc Information

The JDeveloper online help provides reference information for the classes described in this chapter in Javadoc format.

Table 20-2 Location of Javadoc

Class / Interface Location of Javadoc in the Online Help

AbstractDefinition

AbstractImpl

AbstractAdapter

Reference > Oracle ADF Model API Reference > Packages > oracle.adf.model.adapter > Class Summary

StructureDefinition

Reference > Oracle ADF Model API Reference > Packages > oracle.binding.meta > Interface Summary

DataControl

ManagedDataControl

TransactionalDataControl

Reference > Oracle ADF Model API Reference > Packages > oracle.binding > Interface Summary


20.11 Contents of Supporting Files

This section shows the contents of the following files:

20.11.1 sampleDC.xsd

Example 20-37 shows the contents of the sampleDC.xsd file.

Example 20-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>

20.11.2 CSVHandler Class

Example 20-38 shows the contents of the CSVHandler class.

Example 20-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;
  }
   
}

20.11.3 CSVParser

Example 20-39 shows the contents of the CSVParser class.

Example 20-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;
    }
  }
 
}