F Code Samples

This appendix provides the full length code samples referenced from sections throughout this guide.

This appendix includes the following sections:

F.1 Samples for Chapter 4, "Using ADF Faces Client-Side Architecture"

The adf-js-partitions.xml file is involved in the delivery of JavaScript partitions to the client. It groups the JavaScript features in partitions.

Following are code examples for using ADF Faces architecture

F.1.1 The adf-js-partitions.xml File

The default ADF Faces adf-js-partitions.xml file has partitions that you can override by creating your own partitions file. See JavaScript Library Partitioning. The example below shows the default ADF Faces adf-js-partitions.xml file.

<?xml version="1.0" encoding="utf-8"?>
 
<partitions xmlns="http://xmlns.oracle.com/adf/faces/partition">
 
  <partition>
    <partition-name>boot</partition-name>
    <feature>AdfBootstrap</feature>
  </partition>
 
  <partition>
    <partition-name>core</partition-name>
 
    <feature>AdfCore</feature>
 
    <!-- Behavioral component super classes -->
    <feature>AdfUIChoose</feature>
    <feature>AdfUICollection</feature>
    <feature>AdfUICommand</feature>
    <feature>AdfUIDialog</feature>
    <feature>AdfUIDocument</feature>
    <feature>AdfUIEditableValue</feature>
    <feature>AdfUIForm</feature>
    <feature>AdfUIGo</feature>
    <feature>AdfUIInput</feature>
    <feature>AdfUIObject</feature>
    <feature>AdfUIOutput</feature>
    <feature>AdfUIPanel</feature>
    <feature>AdfUIPopup</feature>
    <feature>AdfUISelectBoolean</feature>
    <feature>AdfUISelectInput</feature>
    <feature>AdfUISelectOne</feature>
    <feature>AdfUISelectMany</feature>
    <feature>AdfUIShowDetail</feature>
    <feature>AdfUISubform</feature>
    <feature>AdfUIValue</feature>
 
    <!-- These are all so common that we group them with core -->
    <feature>AdfRichDocument</feature>
    <feature>AdfRichForm</feature>
    <feature>AdfRichPopup</feature>
    <feature>AdfRichSubform</feature>
    <feature>AdfRichCommandButton</feature>
    <feature>AdfRichCommandLink</feature>
 
    <!-- 
      Dialog is currently on every page for messaging.  No use
      in putting these in a separate partition.
    -->
    <feature>AdfRichPanelWindow</feature>
    <feature>AdfRichDialog</feature>
 
    <!-- af:showPopupBehavior is so small/common, belongs in core -->
    <feature>AdfShowPopupBehavior</feature>
  </partition>
 
  <partition>
    <partition-name>accordion</partition-name>
    <feature>AdfRichPanelAccordion</feature>
  </partition>
 
  <partition>
    <partition-name>border</partition-name>
    <feature>AdfRichPanelBorderLayout</feature>
  </partition>
 
  <partition>
    <partition-name>box</partition-name>
    <feature>AdfRichPanelBox</feature>
  </partition>
  
  <partition>
    <partition-name>calendar</partition-name>
    <feature>AdfUICalendar</feature>
    <feature>AdfRichCalendar</feature>
    <feature>AdfCalendarDragSource</feature>
    <feature>AdfCalendarDropTarget</feature>
  </partition>
  
  <partition>
    <partition-name>collection</partition-name>
    <feature>AdfUIDecorateCollection</feature>
    <feature>AdfRichPanelCollection</feature>
  </partition>
 
  <partition>
    <partition-name>color</partition-name>
    <feature>AdfRichChooseColor</feature>
    <feature>AdfRichInputColor</feature>
  </partition>
 
  <partition>
    <partition-name>date</partition-name>
    <feature>AdfRichChooseDate</feature>
    <feature>AdfRichInputDate</feature>
  </partition>
 
  <partition>
    <partition-name>declarativeComponent</partition-name>
    <feature>AdfUIInclude</feature>
    <feature>AdfUIDeclarativeComponent</feature>
    <feature>AdfRichDeclarativeComponent</feature>
  </partition>
 
  <partition>
    <partition-name>detail</partition-name>
    <feature>AdfRichShowDetail</feature>
  </partition>
 
  <partition>
    <partition-name>dnd</partition-name>
    <feature>AdfDragAndDrop</feature>
    <feature>AdfCollectionDragSource</feature>
    <feature>AdfStampedDropTarget</feature>
    <feature>AdfCollectionDropTarget</feature>
    <feature>AdfAttributeDragSource</feature>
    <feature>AdfAttributeDropTarget</feature>
    <feature>AdfComponentDragSource</feature>
    <feature>AdfDropTarget</feature>    
  </partition>
  
  <partition>
    <partition-name>detailitem</partition-name>
    <feature>AdfRichShowDetailItem</feature>
  </partition>
 
  <partition>
    <partition-name>file</partition-name>
    <feature>AdfRichInputFile</feature>
  </partition>
 
  <partition>
    <partition-name>form</partition-name>
    <feature>AdfRichPanelFormLayout</feature>
    <feature>AdfRichPanelLabelAndMessage</feature>
  </partition>
 
  <partition>
    <partition-name>format</partition-name>
    <feature>AdfRichOutputFormatted</feature>
  </partition>
 
  <partition>
    <partition-name>frame</partition-name>
    <feature>AdfRichInlineFrame</feature>
  </partition>
 
  <partition>
    <partition-name>header</partition-name>
    <feature>AdfRichPanelHeader</feature>
    <feature>AdfRichShowDetailHeader</feature>
  </partition>
 
  <partition>
    <partition-name>imagelink</partition-name>
    <feature>AdfRichCommandImageLink</feature>
  </partition>
 
  <partition>
    <partition-name>iedit</partition-name>
    <feature>AdfInlineEditing</feature>
  </partition>
 
  <partition>
    <partition-name>input</partition-name>
    <feature>AdfRichInputText</feature>
    <feature>AdfInsertTextBehavior</feature>
  </partition>
 
  <partition>
    <partition-name>label</partition-name>
    <feature>AdfRichOutputLabel</feature>
  </partition>
 
  <partition>
    <partition-name>list</partition-name>
    <feature>AdfRichPanelList</feature>
  </partition>
 
  <partition>
    <partition-name>lov</partition-name>
    <feature>AdfUIInputPopup</feature>
    <feature>AdfRichInputComboboxListOfValues</feature>
    <feature>AdfRichInputListOfValues</feature>
  </partition>
 
  <partition>
    <partition-name>media</partition-name>
    <feature>AdfRichMedia</feature>
  </partition>
 
  <partition>
    <partition-name>message</partition-name>
    <feature>AdfUIMessage</feature>
    <feature>AdfUIMessages</feature>
    <feature>AdfRichMessage</feature>
    <feature>AdfRichMessages</feature>
  </partition>
 
  <partition>
    <partition-name>menu</partition-name>
    <feature>AdfRichCommandMenuItem</feature>
    <feature>AdfRichGoMenuItem</feature>
    <feature>AdfRichMenuBar</feature>
    <feature>AdfRichMenu</feature>
  </partition>
 
  <partition>
    <partition-name>nav</partition-name>
    <feature>AdfUINavigationPath</feature>
    <feature>AdfUINavigationLevel</feature>
    <feature>AdfRichBreadCrumbs</feature>
    <feature>AdfRichCommandNavigationItem</feature>
    <feature>AdfRichNavigationPane</feature>
  </partition>
 
  <partition>
    <partition-name>note</partition-name>
    <feature>AdfRichNoteWindow</feature>
  </partition>
 
  <partition>
    <partition-name>poll</partition-name>
    <feature>AdfUIPoll</feature>
    <feature>AdfRichPoll</feature>
  </partition>
 
  <partition>
    <partition-name>progress</partition-name>
    <feature>AdfUIProgress</feature>
    <feature>AdfRichProgressIndicator</feature>
  </partition>
 
  <partition>
    <partition-name>print</partition-name>
    <feature>AdfShowPrintablePageBehavior</feature>
  </partition>
 
  <partition>
    <partition-name>scrollComponentIntoView</partition-name>
    <feature>AdfScrollComponentIntoViewBehavior</feature>
  </partition>
 
  <partition>
    <partition-name>query</partition-name>
    <feature>AdfUIQuery</feature>
    <feature>AdfRichQuery</feature>
    <feature>AdfRichQuickQuery</feature>
  </partition>
 
  <partition>
    <partition-name>region</partition-name>
    <feature>AdfUIRegion</feature>
    <feature>AdfRichRegion</feature>
  </partition>
 
  <partition>
    <partition-name>reset</partition-name>
    <feature>AdfUIReset</feature>
    <feature>AdfRichResetButton</feature>
  </partition>
 
  <partition>
    <partition-name>rte</partition-name>
    <feature>AdfRichTextEditor</feature>
    <feature>AdfRichTextEditorInsertBehavior</feature>
  </partition>
 
  <partition>
    <partition-name>select</partition-name>
 
    <feature>AdfRichSelectBooleanCheckbox</feature>
    <feature>AdfRichSelectBooleanRadio</feature>
    <feature>AdfRichSelectManyCheckbox</feature>
    <feature>AdfRichSelectOneRadio</feature>
  </partition>
 
  <partition>
    <partition-name>selectmanychoice</partition-name>
    <feature>AdfRichSelectManyChoice</feature>
  </partition>
 
  <partition>
    <partition-name>selectmanylistbox</partition-name>
    <feature>AdfRichSelectManyListbox</feature>
  </partition>
 
  <partition>
    <partition-name>selectonechoice</partition-name>
    <feature>AdfRichSelectOneChoice</feature>
  </partition>
 
  <partition>
    <partition-name>selectonelistbox</partition-name>
    <feature>AdfRichSelectOneListbox</feature>
  </partition>
 
  <partition>
    <partition-name>shuttle</partition-name>
    <feature>AdfUISelectOrder</feature>
    <feature>AdfRichSelectManyShuttle</feature>
    <feature>AdfRichSelectOrderShuttle</feature>
  </partition>
 
  <partition>
    <partition-name>slide</partition-name>
    <feature>AdfRichInputNumberSlider</feature>
    <feature>AdfRichInputRangeSlider</feature>
  </partition>
 
  <partition>
    <partition-name>spin</partition-name>
    <feature>AdfRichInputNumberSpinbox</feature>
  </partition>
 
  <partition>
    <partition-name>status</partition-name>
    <feature>AdfRichStatusIndicator</feature>
  </partition>
 
  <partition>
    <partition-name>stretch</partition-name>
    <feature>AdfRichDecorativeBox</feature>
    <feature>AdfRichPanelSplitter</feature>
    <feature>AdfRichPanelStretchLayout</feature>
    <feature>AdfRichPanelDashboard</feature>
    <feature>AdfPanelDashboardBehavior</feature>
    <feature>AdfDashboardDropTarget</feature>
  </partition>
 
  <partition>
    <partition-name>tabbed</partition-name>
    <feature>AdfUIShowOne</feature>
    <feature>AdfRichPanelTabbed</feature>
  </partition>
 
  <partition>
    <partition-name>table</partition-name>
    <feature>AdfUIIterator</feature>
    <feature>AdfUITable</feature>
    <feature>AdfUITable2</feature>
    <feature>AdfUIColumn</feature>
    <feature>AdfRichColumn</feature>
    <feature>AdfRichTable</feature>
  </partition>
 
  <partition>
    <partition-name>toolbar</partition-name>
    <feature>AdfRichCommandToolbarButton</feature>
    <feature>AdfRichToolbar</feature>
  </partition>
 
  <partition>
    <partition-name>toolbox</partition-name>
    <feature>AdfRichToolbox</feature>
  </partition>
 
  <partition>
    <partition-name>train</partition-name>
    <feature>AdfUIProcess</feature>
    <feature>AdfRichCommandTrainStop</feature>
    <feature>AdfRichTrainButtonBar</feature>
    <feature>AdfRichTrain</feature>
  </partition>
 
  <partition>
    <partition-name>tree</partition-name>
    <feature>AdfUITree</feature>
    <feature>AdfUITreeTable</feature>
    <feature>AdfRichTree</feature>
    <feature>AdfRichTreeTable</feature>
  </partition>
 
  <!--
    Some components which typically do have client-side representation,
    but small enough that we might as well download in a single partition
    in the event that any of these are needed.
  -->
  <partition>
    <partition-name>uncommon</partition-name>
    <feature>AdfRichGoButton</feature>
    <feature>AdfRichIcon</feature>
    <feature>AdfRichImage</feature>
    <feature>AdfRichOutputText</feature>
    <feature>AdfRichPanelGroupLayout</feature>
    <feature>AdfRichSeparator</feature>
    <feature>AdfRichSpacer</feature>
    <feature>AdfRichGoLink</feature>
  </partition>
 
  <partition>
    <partition-name>eum</partition-name>
    <feature>AdfEndUserMonitoring</feature>
  </partition>
 
  <partition>
    <partition-name>ads</partition-name>
    <feature>AdfActiveDataService</feature>
  </partition>
 
  <partition>
    <partition-name>automation</partition-name>
    <feature>AdfAutomationTest</feature>
  </partition>
 
</partitions>

F.2 Samples for Chapter 29, "Using Map Components"

Using the MapProvider feature, Thematic Map can be configured. The MapProvider APIs like oracle.adf.view.faces.bi.component.thematicMap.mapProvider.MapProvider and orace.adf.view.faces.bi.component.thematicMap.mapProvider.LayerArea allow the custom basemap to be configured and treated like a built-in basemap with all the same functionalities.

Following are code examples for creating DVT map components.

F.2.1 Sample Code for Thematic Map Custom Base Map

When you create a custom base map for a thematic map you extend oracle.adf.view.faces.bi.component.thematicMap.mapProvider.MapProvider. The following is an implementation of the mapProvider class for the Canada custom base map example:

package oracle.adfdemo.view.feature.rich.thematicMap;
 
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
import java.net.URL;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.zip.ZipInputStream;
 
import javax.faces.context.FacesContext;
 
import oracle.adf.share.logging.ADFLogger;
import oracle.adf.view.faces.bi.component.thematicMap.mapProvider.LayerArea;
import oracle.adf.view.faces.bi.component.thematicMap.mapProvider.MapProvider;
import oracle.adf.view.faces.bi.component.thematicMap.mapProvider.utils.MapProviderUtils;
 
/**
 * Sample MapProvider instance that translates GeoJSON data from a zip file to create a custom Canada basemap.
 */
public class DemoMapProvider extends MapProvider {
  private static String zipFile = "/resources/thematicMap/elocation.zip";
  private static final ADFLogger _logger = ADFLogger.createADFLogger(DemoMapProvider.class);
  private List<String> hierarchy;
 
  public DemoMapProvider() {
    hierarchy = new ArrayList<String>();
    hierarchy.add("states");
  }
 
  @Override
  public Collection<LayerArea> getLayerAreas(String layer, Locale locale) {
    String text = getGeoJsonString();
    Map geoJSON = null;
    try {
      geoJSON = (Map) DemoMapProvider.parseJsonString(text);
    } catch (IOException e) {
      _logger.severe("Could not parse geometries.", e);
    }
    return DemoMapProvider.createLayerAreas(geoJSON);
  }
 
  @Override
  public Collection<LayerArea> getChildAreas(String layer, Locale locale, Collection<String> areas) {
    return new ArrayList<LayerArea>();
  }
 
  @Override
  public double getMaxZoomFactor() {
    return 5.0;
  }
 
  @Override
  public List<String> getHierarchy() {
    return hierarchy;
  }
 
  /**
   * Loads the geographic geometries stored in local file and returns as string
   * @return
   */
  private static String getGeoJsonString() {
    StringBuilder sb = new StringBuilder();
    BufferedReader br = null;
    ZipInputStream is = null;
    try {
      URL zipUrl = FacesContext.getCurrentInstance().getExternalContext().getResource(zipFile);
      is = new ZipInputStream(zipUrl.openStream());
      is.getNextEntry();
      br = new BufferedReader(new InputStreamReader(is, "UTF8"));
      String aux = br.readLine();
      while (aux != null) {
        sb.append(aux);
        aux = br.readLine();
      }
    } catch (Exception e) {
      _logger.severe("Could not load geometries from the file " + zipFile, e);
    } finally {
      if (br != null) {
        try {
          is.close();
          br.close();
        } catch (IOException e) { 
          // ignore
        }
      }
    }
    return sb.toString();
  }
 
  /**
   * Parses a JSON string and converts it to the correct Java object
   * @param str The JSON string to parse
   * @return
   * @throws IOException
   */
  private static Object parseJsonString(String str) throws IOException {
    if (str == null)
      return null;
 
    str = str.trim();
 
    char firstChar = str.charAt(0);
    if (firstChar == '[' || firstChar == '{') {
      // Array or Map
      // Count the number of open/close arrays or objects
      int numOpen = 0;
 
      // Quote handling: Count the number of open single/double quotes, except when there is an
      // open one already.  This handles nested single quotes in double quotes, and vice versa.
      int numSingleQuotes = 0;
      int numDoubleQuotes = 0;
 
      // Iterate through and split by pieces
      int prevIndex = 1;
      List<String> parts = new ArrayList<String>();
      for (int i = 1; i < str.length() - 1; i++) {
        char iChar = str.charAt(i);
        if (iChar == '[' || iChar == '{')
          numOpen++;
        else if (iChar == ']' || iChar == '}')
          numOpen--;
        else if (iChar == '\'' && numDoubleQuotes % 2 == 0)
          numSingleQuotes++;
        else if (iChar == '"' && numSingleQuotes % 2 == 0)
          numDoubleQuotes++;
 
        // If split index, store the substring
        if (numOpen == 0 && (numSingleQuotes % 2 == 0 && numDoubleQuotes % 2 == 0) && iChar == ',') {
          parts.add(str.substring(prevIndex, i));
          prevIndex = i + 1;
        }
      }
 
      // Grab the last part if present
      if (prevIndex < str.length() - 1) {
        parts.add(str.substring(prevIndex, str.length() - 1));
      }
 
      // Decode the parts into the result
      if (firstChar == '[') {
        List ret = new ArrayList();
        for (int arrayIndex = 0; arrayIndex < parts.size(); arrayIndex++)
          ret.add(parseJsonString(parts.get(arrayIndex)));
        return ret;
      } else if (firstChar == '{') {
        Map ret = new HashMap();
        for (String part : parts) {
          part = part.trim();
          int colonIndex = part.indexOf(':');
          String mapKey = part.substring(0, colonIndex);
          mapKey = mapKey.substring(1, mapKey.length() - 1); // 1 to -1 to avoid the quotes
          Object mapValue = parseJsonString(part.substring(colonIndex + 1, part.length()));
          ret.put(mapKey, mapValue);
        }
        return ret;
      }
      return null;
    } else if (firstChar == '"') // String
      return str.substring(1, str.length() - 1);
    else if ("true".equals(str))
      return true;
    else if ("false".equals(str))
      return false;
    else
      return Double.parseDouble(str);
  }
 
  /**
   * Converts a GeoJSON object to a list of LayerArea objects
   * @param geoJSON The GeoJSON object containing this basemap layer's area geometry data
   * @return
   */
  private static List<LayerArea> createLayerAreas(Map geoJSON) {
    List territories = Arrays.asList(MapProviderBean.territoryNames);
    HashMap<String, DemoLayerArea> areaMap = new HashMap<String, DemoLayerArea>();
    if (geoJSON != null) {
      List features = (List) geoJSON.get("features");
      int numFeatures = features.size();
      for (int j = 0; j < numFeatures; j++) {
        Map feature = (Map) features.get(j);
        Map properties = (Map) feature.get("properties");
        String label = (String) properties.get("POLYGON_NAME");
        // We just want to render canada
        if (!territories.contains(label))
          continue;
 
        Map geometry = (Map) feature.get("geometry");
 
        Rectangle2D labelBox = null;
        List<Double> labelBoxList = (List<Double>) feature.get("label_box");
        if (labelBoxList != null) {
          int minX = (int) (labelBoxList.get(0) / 2000);
          int minY = (int) (labelBoxList.get(1) / 2000);
          int maxX = (int) (labelBoxList.get(2) / 2000);
          int maxY = (int) (labelBoxList.get(3) / 2000);
          labelBox = new Rectangle(minX, -minY, maxX - minX, maxY - minY);
        }
        DemoLayerArea area = areaMap.get(label);
        if (area != null)
          area.setPath(area.getPath() + DemoMapProvider.simplifyGeometries(geometry));
        else
          areaMap.put(label,
                      new DemoLayerArea(label, null, label, labelBox, DemoMapProvider.simplifyGeometries(geometry),
                                        null));
      }
    }
 
    List<LayerArea> layerAreas = new ArrayList<LayerArea>();
    for (Map.Entry<String, DemoLayerArea> entry : areaMap.entrySet())
      layerAreas.add(entry.getValue());
    return layerAreas;
  }
 
  /**
   * Converts and simplifies area geometries to relative path commands
   * @param geometry The map containing an area's coordinates
   * @return
   */
  private static String simplifyGeometries(Map geometry) {
    StringBuilder sb = new StringBuilder();
    String type = (String) geometry.get("type");
    List coords = (List) geometry.get("coordinates");
    int len = coords.size();
    if ("Polygon".equals(type)) {
      for (int i = 0; i < len; i++)
        sb.append(DemoMapProvider.simplifyGeometriesHelper((List) coords.get(i)));
    } else if ("MultiPolygon".equals(type)) {
      for (int i = 0; i < len; i++) {
        List nestCoords = (List) coords.get(i);
        int nestCoordsLen = nestCoords.size();
        for (int j = 0; j < nestCoordsLen; j++)
          sb.append(DemoMapProvider.simplifyGeometriesHelper((List) coords.get(j)));
      }
    }
    return sb.toString();
  }
 
  /**
   * Helper method for parsing a GeoJSON geometry object
   * @param coords The list of coordinates to simplify and convert to a relative path command
   * @return
   */
  private static String simplifyGeometriesHelper(List coords) {
    List<Point2D> points = new ArrayList<Point2D>();
    int len = coords.size();
    // Convert coordinates to Point2D objects so we can use MapProviderUtils to simplify area gemoetries
    // Also reduce data precision by dividing by 2000
    for (int i = 0; i < len; i += 2)
      points.add(new Point2D.Double(Math.floor((Double) coords.get(i) / 2000),
                                    -Math.floor((Double) coords.get(i + 1) / 2000)));
    return MapProviderUtils.convertToPath(points);
  }
}

F.2.2 Sample Code for Thematic Map Custom Base Map Area Layer

When you create a custom base map for a thematic map you extend oracle.adf.view.faces.bi.component.thematicMap.mapProvider.LayerArea to get the data that the thematic map component requires to render a dvt:areaLayer. The following is an implementation of the layerArea class for the Canada custom base map example:

package oracle.adfdemo.view.feature.rich.thematicMap;
 
import java.awt.geom.Rectangle2D;
 
import oracle.adf.view.faces.bi.component.thematicMap.mapProvider.LayerArea;
 
public class DemoLayerArea extends LayerArea {
 
  private String id;
  private String shortLabel;
  private String longLabel;
  private Rectangle2D labelBox;
  private String path;
  private String parent;
  
  public DemoLayerArea(String id, String shortLabel, String longLabel, Rectangle2D labelBox, String path, String parent) {
    this.id = id;
    this.shortLabel = shortLabel;
    this.longLabel = longLabel;
    this.labelBox = labelBox;
    this.path = path;
    this.parent = parent;
  }
 
  @Override
  public String getId() {
    return id;
  }
 
  @Override
  public String getShortLabel() {
    return shortLabel;
  }
 
  @Override
  public String getLongLabel() {
    return longLabel;
  }
 
  @Override
  public Rectangle2D getLabelBox() {
    return labelBox;
  }
  
  @Override
  public String getPath() {
    return path;
  }
  
  public void setPath(String path) {
    this.path = path;
  }
 
  @Override
  public String getParent() {
    return parent;
  }
}

F.3 Samples for Chapter 31, "Using Treemap and Sunburst Components"

In order to add data to the treemap or sunburst using UI-first development, create the classes, managed beans, and methods that will create the tree model and reference the classes, beans, or methods in JDeveloper.

Following are code examples for creating treemap and sunburst components.

F.3.1 Sample Code for Treemap and Sunburst Census Data Example

When you create a treemap or sunburst using UI-first development, you can use Java classes and managed beans to define the tree node and tree model, populate the tree with data and add additional methods as needed to configure the treemap or sunburst.

The example below shows a code sample defining the tree node in the census data example. Note that the required settings for label, size, and color are passed in as parameters to the tree node.

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
 
public class TreeNode {
  private final String m_text;
  private final Number m_size;
  private final Color m_color;
  private final List<TreeNode> m_children = new ArrayList<TreeNode>();
  
  public TreeNode(String text, Number size, Color color) {
    m_text = text;
    m_size = size;
    m_color = color;
  }
  public String getText() {
    return m_text;
  }
  public Number getSize() {
    return m_size;
  }
  public Color getColor() {
    return m_color;
  }
  public void addChild(TreeNode child) {
    m_children.add(child);
  }
  public void addChildren(List<TreeNode> children) {
    m_children.addAll(children);
  }
  public List<TreeNode> getChildren() {
    return m_children;
  }
  @Override
  public String toString() {
    return m_text + ": " + m_color + " " + Math.round(m_size.doubleValue());
  }
}

To supply data to the treemap or sunburst in UI-first development, add a class or managed bean to your application that extends the tree node in the above example and populates it with data. The class to set up the tree model must be an implementation of the org.apache.myfaces.trinidad.model.TreeModel class. Once the tree model is defined, create a method that implements the org.apache.myfaces.trinidad.model.ChildPropertyTreeModel to complete the tree model.

The example below shows a sample class that sets up the root and child node structure, populates the child levels with data and defines the color and node sizes in the census data example.

import java.awt.Color;
 
import java.util.ArrayList;
import java.util.List;
 
import org.apache.myfaces.trinidad.model.ChildPropertyTreeModel;
import org.apache.myfaces.trinidad.model.TreeModel;
 
 
public class CensusData {
 
  public static TreeModel getUnitedStatesData() {
    return getModel(ROOT);
  }
 
  public static TreeModel getRegionWestData() {
    return getModel(REGION_W);
  }
 
  public static TreeModel getRegionNortheastData() {
    return getModel(REGION_NE);
  }
 
  public static TreeModel getRegionMidwestData() {
    return getModel(REGION_MW);
  }
 
  public static TreeModel getRegionSouthData() {
    return getModel(REGION_S);
  }
 
  public static TreeModel getDivisionPacificData() {
    return getModel(DIVISION_P);
  }
 
  private static TreeModel getModel(DataItem rootItem) {
    TreeNode root = getTreeNode(rootItem);
    return new ChildPropertyTreeModel(root, "children");
  }
 
  private static TreeNode getTreeNode(DataItem dataItem)
  {
    // Create the node itself
    TreeNode node = new CensusTreeNode(dataItem.getName(),
                        dataItem.getPopulation(),
                        getColor(dataItem.getIncome(), MIN_INCOME, MAX_INCOME),
                        dataItem.getIncome());
 
    // Create its children
    List<TreeNode> children = new ArrayList<TreeNode>();
    for(DataItem childItem : dataItem.children) {
      children.add(getTreeNode(childItem));
    }
 
    // Add the children and return
    node.addChildren(children);
    return node;
  }
 
  private static Color getColor(double value, double min, double max) {
    double percent = Math.max((value - min) / max, 0);
    if(percent > 0.5) {
      double modifier = (percent - 0.5) * 2;
      return new Color((int)(modifier*102), (int)(modifier*153), (int)(modifier*51));
    }
    else {
      double modifier = percent *2;
      return new Color((int)(modifier*204), (int)(modifier*51), 0);
    }
  }
 
  public static class DataItem {
    private final String name;
    private final int population;
    private final int income;
    private final List<DataItem> children;
 
    public DataItem(String name, int population, int income) {
      this.name = name;
      this.population = population;
      this.income = income;
      this.children = new ArrayList<DataItem>();
    }
 
    public void addChild(DataItem child) {
      this.children.add(child);
    }
 
    public String getName() {
      return name;
    }
 
    public int getPopulation() {
      return population;
    }
 
    public int getIncome() {
      return income;
    }
 
    public List<CensusData.DataItem> getChildren() {
      return children;
    }
  }
 
  private static final int MIN_INCOME = 0;
  private static final int MAX_INCOME = 70000;
 
  private static final DataItem ROOT = new DataItem("United States", 301461533, 51425);
 
  private static final DataItem REGION_NE = new DataItem("Northeast Region", 54906297, 57208);
  private static final DataItem REGION_MW = new DataItem("Midwest Region", 66336038, 49932);
  private static final DataItem REGION_S = new DataItem("South Region", 110450832, 47204);
  private static final DataItem REGION_W = new DataItem("West Region", 69768366, 56171);
 
  private static final DataItem DIVISION_NE = new DataItem("New England", 14315257, 61511);
  private static final DataItem DIVISION_MA = new DataItem("Middle Atlantic", 40591040, 55726);
  private static final DataItem DIVISION_ENC = new DataItem("East North Central", 46277998, 50156);
  private static final DataItem DIVISION_WNC = new DataItem("West North Central", 20058040, 49443);
  private static final DataItem DIVISION_SA = new DataItem("South Atlantic", 57805475, 50188);
  private static final DataItem DIVISION_ESC = new DataItem("East South Central", 17966553, 41130);
  private static final DataItem DIVISION_WSC = new DataItem("West South Central", 34678804, 45608);
  private static final DataItem DIVISION_M = new DataItem("Mountain", 21303294, 51504);
  private static final DataItem DIVISION_P = new DataItem("Pacific", 48465072, 58735);
 
  static {
    // Set up the regions
    ROOT.addChild(REGION_NE);
    ROOT.addChild(REGION_MW);
    ROOT.addChild(REGION_S);
    ROOT.addChild(REGION_W);
 
    // Set up the divisions
    REGION_NE.addChild(DIVISION_NE);
    REGION_NE.addChild(DIVISION_MA);
    REGION_MW.addChild(DIVISION_ENC);
    REGION_MW.addChild(DIVISION_WNC);
    REGION_S.addChild(DIVISION_SA);
    REGION_S.addChild(DIVISION_ESC);
    REGION_S.addChild(DIVISION_WSC);
    REGION_W.addChild(DIVISION_M);
    REGION_W.addChild(DIVISION_P);
 
    // Set up the states
    DIVISION_NE.addChild(new DataItem("Connecticut", 3494487, 67721));
    DIVISION_NE.addChild(new DataItem("Maine", 1316380, 46541));
    DIVISION_NE.addChild(new DataItem("Massachusetts", 6511176, 64496));
    DIVISION_NE.addChild(new DataItem("New Hampshire", 1315419, 63033));
    DIVISION_NE.addChild(new DataItem("Rhode Island", 1057381, 55569));
    DIVISION_NE.addChild(new DataItem("Vermont", 620414, 51284));
 
    DIVISION_MA.addChild(new DataItem("New Jersey", 8650548, 68981));
    DIVISION_MA.addChild(new DataItem("New York", 19423896, 55233));
    DIVISION_MA.addChild(new DataItem("Pennsylvania", 12516596, 49737));
 
    DIVISION_ENC.addChild(new DataItem("Indiana", 6342469, 47465));
    DIVISION_ENC.addChild(new DataItem("Illinois", 12785043, 55222));
    DIVISION_ENC.addChild(new DataItem("Michigan", 10039208, 48700));
    DIVISION_ENC.addChild(new DataItem("Ohio", 11511858, 47144));
    DIVISION_ENC.addChild(new DataItem("Wisconsin", 5599420, 51569));
 
    DIVISION_WNC.addChild(new DataItem("Iowa", 2978880, 48052));
    DIVISION_WNC.addChild(new DataItem("Kansas", 2777835, 48394));
    DIVISION_WNC.addChild(new DataItem("Minnesota", 5188581, 57007));
    DIVISION_WNC.addChild(new DataItem("Missouri", 5904382, 46005));
    DIVISION_WNC.addChild(new DataItem("Nebraska", 1772124, 47995));
    DIVISION_WNC.addChild(new DataItem("North Dakota", 639725, 45140));
    DIVISION_WNC.addChild(new DataItem("South Dakota", 796513, 44828));
 
    DIVISION_SA.addChild(new DataItem("Delaware", 863832, 57618));
    DIVISION_SA.addChild(new DataItem("District of Columbia", 588433, 56519));
    DIVISION_SA.addChild(new DataItem("Florida", 18222420, 47450));
    DIVISION_SA.addChild(new DataItem("Georgia", 9497667, 49466));
    DIVISION_SA.addChild(new DataItem("Maryland", 5637418, 69475));
    DIVISION_SA.addChild(new DataItem("North Carolina", 9045705, 45069));
    DIVISION_SA.addChild(new DataItem("South Carolina", 4416867, 43572));
    DIVISION_SA.addChild(new DataItem("Virginia", 7721730, 60316));
    DIVISION_SA.addChild(new DataItem("West Virginia", 1811403, 37356));
 
    DIVISION_ESC.addChild(new DataItem("Alabama", 4633360, 41216));
    DIVISION_ESC.addChild(new DataItem("Kentucky", 4252000, 41197));
    DIVISION_ESC.addChild(new DataItem("Mississippi", 2922240, 36796));
    DIVISION_ESC.addChild(new DataItem("Tennessee", 6158953, 42943));
 
    DIVISION_WSC.addChild(new DataItem("Arkansas", 2838143, 38542));
    DIVISION_WSC.addChild(new DataItem("Louisiana", 4411546, 42167));
    DIVISION_WSC.addChild(new DataItem("Oklahoma", 3610073, 41861));
    DIVISION_WSC.addChild(new DataItem("Texas", 23819042, 48199));
 
    DIVISION_M.addChild(new DataItem("Arizona", 6324865, 50296));
    DIVISION_M.addChild(new DataItem("Colorado", 4843211, 56222));
    DIVISION_M.addChild(new DataItem("Idaho", 1492573, 46183));
    DIVISION_M.addChild(new DataItem("Montana", 956257, 43089));
    DIVISION_M.addChild(new DataItem("Nevada", 2545763, 55585));
    DIVISION_M.addChild(new DataItem("New Mexico", 1964860, 42742));
    DIVISION_M.addChild(new DataItem("Utah", 2651816, 55642));
    DIVISION_M.addChild(new DataItem("Wyoming", 523949, 51990));
 
    DIVISION_P.addChild(new DataItem("Alaska", 683142, 64635));
    DIVISION_P.addChild(new DataItem("California", 36308527, 60392));
    DIVISION_P.addChild(new DataItem("Hawaii", 1280241, 64661));
    DIVISION_P.addChild(new DataItem("Oregon", 3727407, 49033));
    DIVISION_P.addChild(new DataItem("Washington", 6465755, 56384));
  }
 
  public static class CensusTreeNode extends TreeNode {
    private int income;
 
    public CensusTreeNode(String text, Number size, Color color, int income) {
      super(text, size, color);
      this.income = income;
    }
 
    public int getIncome() {
      return income;
    }
  }
}

Finally, to complete the tree model in UI-first development, add a managed bean to your application that references the class or bean that contains the data and, optionally, add any other methods to customize the treemap or sunburst.

The example below shows a code sample that will instantiate the census treemap and populate it with census data. The example also includes a sample method (convertToString) that will convert the treemap node's row data to a string for label display.

import org.apache.myfaces.trinidad.component.UIXHierarchy;
import org.apache.myfaces.trinidad.model.RowKeySet;
import org.apache.myfaces.trinidad.model.TreeModel;
import oracle.adf.view.faces.bi.component.treemap.UITreemap;
 
public class SampleTreemap {
  // Data Model Attrs
  private TreeModel currentModel;
  private final CensusData censusData = new CensusData();
  private String censusRoot = "United States";
  private UITreemap treemap;
 
  public TreeModel getCensusRootData() {
    return censusData.getUnitedStatesData();
  }
  public TreeModel getCensusData() {
    if ("West Region".equals(censusRoot))
      return censusData.getRegionWestData();
    else if ("South Region".equals(censusRoot))
      return censusData.getRegionSouthData();
    else if ("Midwest Region".equals(censusRoot))
      return censusData.getRegionMidwestData();
    else if ("Northeast Region".equals(censusRoot))
      return censusData.getRegionNortheastData();
    else if ("Pacific Division".equals(censusRoot))
      return censusData.getDivisionPacificData();
    else
      return censusData.getUnitedStatesData();
  }
  public TreeModel getData() {
    // Return cached data model if available
    if(currentModel != null)
      return currentModel;
    currentModel = getCensusData();
    return currentModel;
  }
  public void setCensusRoot(String censusRoot) {
    this.censusRoot = censusRoot;
  }
  public String getCensusRoot() {
    return censusRoot;
  }
  //Converts the rowKeySet into a string of node text labels.
  public static String convertToString(RowKeySet rowKeySet, UIXHierarchy hierarchy) {
    StringBuilder s = new StringBuilder();
    if (rowKeySet != null) {
      for (Object rowKey : rowKeySet) {
        TreeNode rowData = (TreeNode)hierarchy.getRowData(rowKey);
        s.append(rowData.getText()).append(", ");
      }
      // Remove the trailing comma
      if (s.length() > 0)
        s.setLength(s.length() - 2);
    }
    return s.toString();
  }
  public void setTreemap(UITreemap treemap) {
        this.treemap = treemap;
  }
  public UITreemap getTreemap() {
        return treemap;
  }
}

The code to set up the sunburst census sample is nearly identical since both components use the same tree model. See Code Sample for Sunburst Managed Bean for an example.

F.3.2 Code Sample for Sunburst Managed Bean

The following code sample instantiates the census sunburst and populates it with census data. The example also includes a sample method (convertToString) that will convert the sunburst node's row data to a string for label display.

import oracle.adf.view.faces.bi.component.sunburst.UISunburst;
import org.apache.myfaces.trinidad.component.UIXHierarchy;
import org.apache.myfaces.trinidad.model.RowKeySet;
import org.apache.myfaces.trinidad.model.TreeModel;
 
public class SunburstSample {
  // Components
  private UISunburst sunburst;
  // Attributes
  private TreeModel currentModel;
  private final CensusData censusData = new CensusData();
  private String censusRoot = "United States";
  
  public TreeModel getCensusRootData() {
    return censusData.getUnitedStatesData();
  }
  public TreeModel getCensusData() {
    if ("West Region".equals(censusRoot))
      return censusData.getRegionWestData();
    else if ("South Region".equals(censusRoot))
      return censusData.getRegionSouthData();
    else if ("Midwest Region".equals(censusRoot))
      return censusData.getRegionMidwestData();
    else if ("Northeast Region".equals(censusRoot))
      return censusData.getRegionNortheastData();
    else if ("Pacific Division".equals(censusRoot))
      return censusData.getDivisionPacificData();
    else
      return censusData.getUnitedStatesData();
  }
  public TreeModel getData() {
    // Return cached data model if available
    if(currentModel != null)
      return currentModel;
    currentModel = getCensusData();
    return currentModel;
  }
  public void setCensusRoot(String censusRoot) {
    this.censusRoot = censusRoot;
  }
  public String getCensusRoot() {
    return censusRoot;
  }
  public static String convertToString(RowKeySet rowKeySet, UIXHierarchy hierarchy) {
    StringBuilder s = new StringBuilder();
    if (rowKeySet != null) {
      for (Object rowKey : rowKeySet) {
        TreeNode rowData = (TreeNode)hierarchy.getRowData(rowKey);
        s.append(rowData.getText()).append(", ");
      }
      // Remove the trailing comma
      if (s.length() > 0)
        s.setLength(s.length() - 2);
    }
    return s.toString();
  }  
  public void setSunburst(UISunburst sunburst) {    
    this.sunburst = sunburst;
  }
  public UISunburst getSunburst() {
    return sunburst;
  }
}

F.4 Samples for Chapter 32, "Using Diagram Components"

The Create Diagram wizard in JDeveloper provides a default client layout that you can use or edit to specify the layout of your diagram. The default layout is based on force-directed graph drawing algorithms.

Following are code examples for creating diagram components using the DVT diagram layout framework.

F.4.1 Code Sample for Default Client Layout

When you create a diagram using UI-first development, JDeveloper provides a default client layout option based on force-directed graph drawing algorithms, which can be used and edited.

var Application1DiagramLayout = function(layoutContext, optLinkLength, initialTemp) {
  this._layoutContext = layoutContext;
  this._optLinkLength = optLinkLength;
  this._initialTemp = initialTemp;
};
 
//pad factor for the node size
Application1DiagramLayout.PAD_FACTOR = 1.2;
//initial temperature factor - percent of ideal viewport dimension
Application1DiagramLayout.INIT_TEMP_FACTOR = .25; 
//number of iterations to run
Application1DiagramLayout.ITERATIONS = 200; 
 
/**
 * Main function that does the force directed layout (Layout entry point)
 * See algorithm in "Graph Drawing by Force-directed Placement" by Thomas M. J. Fruchterman and Edward M. Reingold
 * @param {DvtDiagramLayoutContext} layoutContext object that defines a context for layout call
 */
Application1DiagramLayout.forceDirectedLayout = function(layoutContext)
{
  //pretend that the layout area is just big enough to fit all the nodes
  var maxBounds = Application1DiagramLayout.getMaxNodeBounds(layoutContext);
  var nodeCount = layoutContext.getNodeCount();
  var area = nodeCount * (Application1DiagramLayout.PAD_FACTOR * maxBounds.w) * (Application1DiagramLayout.PAD_FACTOR * maxBounds.h);
  var initialTemp = Application1DiagramLayout.INIT_TEMP_FACTOR * Math.sqrt(area);
    
  //optimal link length - default is just the size of an ideal grid cell  
  var layoutAttrs = layoutContext.getLayoutAttributes();
  var optLinkLength = (layoutAttrs && layoutAttrs["optimalLinkLength"]) ? parseFloat(layoutAttrs["optimalLinkLength"]) : Math.sqrt(area / nodeCount);
 
  //initialize and run the layout  
  var layout = new Application1DiagramLayout(layoutContext, optLinkLength, initialTemp);
  layout.layoutNodes();
  layout.layoutLinks(); 
  
  //position labels
  layout.positionNodeLabels(); 
  layout.positionLinkLabels();
};
 
/**
 * Layout nodes
 */
Application1DiagramLayout.prototype.layoutNodes = function () {
  this.initForceDirectedPositions();
  var iter = Application1DiagramLayout.ITERATIONS; 
  var temp = this._initialTemp;  
  for (var i = 0; i < iter; i++) {
    this.runForceDirectedIteration(temp);
    //after each iteration, decrease the temperature - we do it linearly
    temp = this._initialTemp * (1 - (i + 1)/iter);
  }
};
 
/**
 * Reposition nodes using force directed algorithm for a single iteration
 * @param {number} t temperature for the iteration that used to limit node displacement
 */
Application1DiagramLayout.prototype.runForceDirectedIteration = function(t)
{    
  //calculate the repulsive force between each two nodes
  var nodeCount = this._layoutContext.getNodeCount();
  for (var ni = 0; ni < nodeCount; ni++) {
    var node = this._layoutContext.getNodeByIndex(ni);
    node.disp = new DvtDiagramPoint(0, 0);
    for (var ni2 = 0; ni2 < nodeCount; ni2++) {
      if (ni == ni2)
        continue;
      var node2 = this._layoutContext.getNodeByIndex(ni2); 
      
      var difference = this._subtractVectors(node.getPosition(), node2.getPosition());
      var distance = this._vectorLength(difference);
      var repulsion = (this._optLinkLength * this._optLinkLength) / distance;
      node.disp = this._addVectors(node.disp, this._scaleVector(difference, repulsion / distance ));
    }
  }
  
  //calculate the attractive force between linked nodes
  var linkCount = this._layoutContext.getLinkCount();
  for (var li = 0; li < linkCount; li++) {
    var link = this._layoutContext.getLinkByIndex(li);
    var node = this._getNodeAtCurrentLevel (link.getStartId());
    var node2 = this._getNodeAtCurrentLevel (link.getEndId());
    if (!node || !node2)
      continue;
    var difference = this._subtractVectors(node.getPosition(), node2.getPosition());
    var distance = this._vectorLength(difference);
    var attraction = (distance * distance) / this._optLinkLength;
    node.disp =  this._subtractVectors(node.disp, this._scaleVector(difference, attraction / distance ) );
    node2.disp = this._addVectors(node2.disp, this._scaleVector(difference, attraction / distance ) );    
  }
 
  //limit node displacement by the temperature t and set the position
  for (var ni = 0; ni < nodeCount; ni++) {
    var node = this._layoutContext.getNodeByIndex(ni);
    this._addGravity(node);
    var distance = this._vectorLength(node.disp);
    var pos = this._addVectors(node.getPosition(), this._scaleVector(node.disp, Math.min(distance, t) / distance));
    node.setPosition(pos);
  }
};
 
/**
 * Adds gravity force that attracts a node to the center, the gravity force does not allow disconnected nodes and branches to be pushed far away from the center
 * @param {DvtDiagramLayoutContextNode} node object that defines node context for the layout
 */
Application1DiagramLayout.prototype._addGravity = function(node) {
  var gravityAdjustment = .2; 
  var distance = this._vectorLength(node.getPosition()); //distance from the center (0,0)
  var attraction = (distance * distance) / this._optLinkLength;
  node.disp =  this._subtractVectors(node.disp, this._scaleVector(node.getPosition(), attraction / distance * gravityAdjustment ) );
};
 
/**
 * Initializes node positions - node positions in force directed layout must be initialized such that no
 * two nodes have the same position. Position nodes in a circle.
 */
Application1DiagramLayout.prototype.initForceDirectedPositions = function() {
  var nodeCount = this._layoutContext.getNodeCount();
  var angleStep = 2*Math.PI / nodeCount;
  var radius = this._optLinkLength;
  for (var ni = 0; ni < nodeCount; ni++) {
    var x = radius * Math.cos(angleStep * ni);
    var y = radius * Math.sin(angleStep * ni);
    var node = this._layoutContext.getNodeByIndex(ni);
    node.setPosition(new DvtDiagramPoint(x, y));  
  }
};
 
/**
 * Calculate vector length
 * @param {DvtDiagramPoint} p vector
 * @return {number} vector length
 */
Application1DiagramLayout.prototype._vectorLength = function(p) {
  return Math.sqrt(p.x * p.x + p.y * p.y);
};
 
/**
 * Scale vector
 * @param {DvtDiagramPoint} p vector
 * @param {number} scale scale
 * @return {DvtDiagramPoint} resulting vector
 */
Application1DiagramLayout.prototype._scaleVector = function(p, scale) {
  return new DvtDiagramPoint(p.x * scale, p.y * scale);
};
 
/**
 * Adds vectors
 * @param {DvtDiagramPoint} p1 vector
 * @param {DvtDiagramPoint} p2 vector
 * @return {DvtDiagramPoint} resulting vector
 */
Application1DiagramLayout.prototype._addVectors = function(p1, p2) {
  return new DvtDiagramPoint(p1.x + p2.x, p1.y + p2.y);
};
 
/**
 * Subtract vectors
 * @param {DvtDiagramPoint} p1 vector
 * @param {DvtDiagramPoint} p2 vector
 * @return {DvtDiagramPoint} resulting vector 
 */
Application1DiagramLayout.prototype._subtractVectors = function(p1, p2) {
  return new DvtDiagramPoint(p1.x - p2.x, p1.y - p2.y);
};
 
/**
 * Finds a node for the link by the node id. In case of a link that does not connect nodes across containers, that will be a node itself. 
 * In case when a link connects nodes across containers, that might be one of the ancestor nodes - the node that has been processed at the current level.
 * @param {string} nodeId id of the node to check
 */
Application1DiagramLayout.prototype._getNodeAtCurrentLevel = function(nodeId) {
  var node;
  do {
    if (!nodeId)
      return null;
    node = this._layoutContext.getNodeById(nodeId);
    nodeId = node.getContainerId();
  } while (!node.disp);
  return node;
};
 
/**
 * Create links
 */
Application1DiagramLayout.prototype.layoutLinks = function () {
  for (var li = 0;li < this._layoutContext.getLinkCount();li++) {
    var link = this._layoutContext.getLinkByIndex(li);
    link.setPoints(this.getEndpoints(link));
  }
};
 
/**
 * Get endpoints for the link
 * @param {DvtDiagramLayoutContextLink} link object  that defines link context for the layout
 * @return {array} an array that contains the start X, Y coordinates and the end X, Y coordinates for the link
 */
Application1DiagramLayout.prototype.getEndpoints = function (link) {
  var n1 = this._layoutContext.getNodeById(link.getStartId());
  var n2 = this._layoutContext.getNodeById(link.getEndId());
  
  var n1Position = n1.getPosition();
  var n2Position = n2.getPosition();
  if (n1.getContainerId() || n2.getContainerId()) { //for cross-container link
    n1Position = this._layoutContext.localToGlobal(new DvtDiagramPoint(0, 0), n1);
    n2Position = this._layoutContext.localToGlobal(new DvtDiagramPoint(0, 0), n2);
  }
  
  var b1 = n1.getContentBounds();
  var b2 = n2.getContentBounds();
  
  var startX = n1Position.x + b1.x + .5 * b1.w;
  var startY = n1Position.y + b1.y + .5 * b1.h;
  var endX = n2Position.x + b2.x + .5 * b2.w;
  var endY = n2Position.y + b2.y + .5 * b2.h;  
  
  b1 = new DvtDiagramRectangle(n1Position.x + b1.x, n1Position.y + b1.y, b1.w, b1.h);
  b2 = new DvtDiagramRectangle(n2Position.x + b2.x, n2Position.y + b2.y, b2.w, b2.h);  
  var startP = this._findLinkNodeIntersection(b1, startX, startY, endX, endY, link.getStartConnectorOffset());
  var endP = this._findLinkNodeIntersection(b2, endX, endY, startX, startY, link.getEndConnectorOffset());
  return [startP.x, startP.y, endP.x, endP.y];
};
 
/**
 * Find a point where a link line intersects the node boundary - use that point as the start or the end connection point
 * @param {DvtDiagramRectangle} rect the bounds of the node content
 * @param {number} startX x coordinate for the line start
 * @param {number} startY y coordinate for the line start
 * @param {number} endX x coordinate for the line end
 * @param {number} endY y coordinate for the line end
 * @param {number} connOffset the offset of the start connector
 * @return {DvtDiagramPoint} a point where a link line intersects the node boundary
 */
Application1DiagramLayout.prototype._findLinkNodeIntersection = function (rect, startX, startY, endX, endY, connOffset) {
 
  var lineAngle = Math.atan2(endY - startY, endX - startX);
  var cornerAngle = Math.atan2(rect.h, rect.w); // rectangle diagonal from top left to right bottom
  var bottomRightAngle = cornerAngle; 
  var bottomLeftAngle = Math.PI - bottomRightAngle;
  var topRightAngle =  - bottomRightAngle;
  var topLeftAngle =  - bottomLeftAngle;
  var x = 0, y = 0;
  if (lineAngle >= topLeftAngle && lineAngle <= topRightAngle) {    // side top
    x = rect.x + rect.w * .5 + Math.tan(Math.PI / 2 - lineAngle) * (-rect.h * .5);
    y = rect.y;          
  }        
  else if (lineAngle <= bottomLeftAngle && lineAngle >= bottomRightAngle) {     // side bottom
    x = rect.x + rect.w * .5 + Math.tan(Math.PI / 2 - lineAngle) * (rect.h * .5);
    y = rect.y + rect.h;
  }
  else if (lineAngle <= bottomRightAngle && lineAngle >= topRightAngle) { // side right
    x = rect.x + rect.w;
    y = rect.y + rect.h * .5 + Math.tan(lineAngle) * (rect.w * .5);
  }        
  else {                                                                  //side left
    x = rect.x;
    y = rect.y + rect.h * .5 + Math.tan(lineAngle) * (- rect.w * .5);          
  }
        
  if (connOffset) {
    x += Math.cos(lineAngle) * connOffset;
    y += Math.sin(lineAngle) * connOffset;
  }
  return new DvtDiagramPoint(x, y);  
};
 
/**
 * Center node label below the node
 */
Application1DiagramLayout.prototype.positionNodeLabels = function () {
  for (var ni = 0; ni < this._layoutContext.getNodeCount();ni++) {
    var node = this._layoutContext.getNodeByIndex(ni);
    var nodeLabelBounds = node.getLabelBounds();
    if (nodeLabelBounds) {
      var nodeBounds = node.getContentBounds();
      var nodePos = node.getPosition();      
      var labelX = nodeBounds.x + nodePos.x + .5 * (nodeBounds.w - nodeLabelBounds.w);
      var labelY = nodeBounds.y + nodePos.y + nodeBounds.h;
      node.setLabelPosition(new DvtDiagramPoint(labelX, labelY));
    }
  }
};
 
/**
 * Position link label at the link center
 */
Application1DiagramLayout.prototype.positionLinkLabels = function (layoutContext) {
  for (var ni = 0;ni < this._layoutContext.getLinkCount();ni++) {
    var link = this._layoutContext.getLinkByIndex(ni);
    var linkLabelBounds = link.getLabelBounds();
    if (linkLabelBounds) {
      var points = link.getPoints();
      if (points.length >=4) {
        var startX = points[0], endX = points[points.length-2];
        var startY = points[1], endY = points[points.length-1];
        var labelX = startX + .5 * (endX - startX - linkLabelBounds.w);
        var labelY = startY + .5 * (endY - startY - linkLabelBounds.h);
        link.setLabelPosition(new DvtDiagramPoint(labelX, labelY));
      }
    }
  }
};
 
/**
 * Helper function that finds max node width and height
 * @param {DvtDiagramLayoutContext} layoutContext Object that defines a context for layout call
 * @return {DvtDiagramRectangle} a rectangle that represent a node with max width and max height
 */
Application1DiagramLayout.getMaxNodeBounds = function (layoutContext) {
  var nodeCount = layoutContext.getNodeCount();
  var maxW = 0 , maxH = 0;
  for (var ni = 0;ni < nodeCount;ni++) {
    var node = layoutContext.getNodeByIndex(ni);
    var bounds = node.getContentBounds();
    maxW = Math.max(bounds.w, maxW);
    maxH = Math.max(bounds.h, maxH);
  }
  return new DvtDiagramRectangle(0, 0, maxW, maxH);
};

F.4.2 Code Sample for Simple Circle Layout

For a simple circle layout of nodes and links.

var LearningDiagramLayouts = {
    simpleVertical : function (layoutContext) {
        var largestNodeBounds = LearningDiagramLayouts.getMaxNodeBounds(layoutContext);
        var nodeGap = (2 * largestNodeBounds.h);
        var nodeCount = layoutContext.getNodeCount();
        var linkCount = layoutContext.getLinkCount();
        var xPos = 0;
        var yPos = 0;
        var labelXPos = largestNodeBounds.w + 10;
 
        for (var i = 0;i < nodeCount;i++) {
            var node = layoutContext.getNodeByIndex(i);
            var nodeHeight = node.getContentBounds().h;
            node.setPosition(new DvtDiagramPoint(xPos, yPos));
 
            var labelHeight = node.getLabelBounds().h;
            var labelYPos = yPos + ((nodeHeight - labelHeight) / 2);
 
            node.setLabelPosition(new DvtDiagramPoint(labelXPos, labelYPos));
            yPos += (nodeHeight + nodeGap);
        }
 
        var linkGap = 4;
        var largestLinkLabelBounds = LearningDiagramLayouts.getMaxLinkLabelBounds(layoutContext);
        for (var j = 0;j < linkCount;j++) {
            var link = layoutContext.getLinkByIndex(j);
            var startNode = layoutContext.getNodeById(link.getStartId());
            var endNode = layoutContext.getNodeById(link.getEndId());
            var linkStartPos = LearningDiagramLayouts.getNodeEdgeMidPoint(startNode, "s");
            var linkEndPos = LearningDiagramLayouts.getNodeEdgeMidPoint(endNode, "n");
            
            var linkLabelXPos;
            var linkLabelYPos = linkStartPos.y + ((linkEndPos.y - linkStartPos.y - link.getLabelBounds().h) / 2);
 
            if (linkEndPos.y > linkStartPos.y) {
                link.setPoints([linkStartPos.x, (linkStartPos.y + linkGap), linkEndPos.x, (linkEndPos.y - linkGap)]);
                linkLabelXPos = linkStartPos.x - (link.getLabelBounds().w + 20);
            } else {
                // need to re-route in this case 
                // Segment 1 - keep heading down for a short distance 
                var turn1 = new DvtDiagramPoint();
                turn1.x = linkStartPos.x;
                turn1.y = linkStartPos.y + (largestNodeBounds.h/2);
                // Segment 2 - head left far enough to avoid overlap with the other link labels
                var turn2 = new DvtDiagramPoint();
                turn2.x = turn1.x - (largestLinkLabelBounds.w + 40);
                turn2.y = turn1.y;
                // Segment 3 - Back up the diagram to a point equally above the end node
                var turn3 = new DvtDiagramPoint();
                turn3.x = turn2.x;
                turn3.y = linkEndPos.y - (largestNodeBounds.h/2);
                // Segment 4 - Back to the center line
                var turn4 = new DvtDiagramPoint();
                turn4.x = linkEndPos.x;
                turn4.y = turn3.y;
                // Segment 5 - And down to the end node for which 
                // we already have the coordinates
                
                //Now pass in the path:
                link.setPoints([linkStartPos.x, (linkStartPos.y + linkGap), 
                                                 turn1.x, turn1.y,
                                                 turn2.x, turn2.y,
                                                 turn3.x, turn3.y,
                                                 turn4.x, turn4.y,
                                                 linkEndPos.x, (linkEndPos.y - linkGap)]);
                                                 
               //Finally work out the X position of the label in this case
                linkLabelXPos = turn3.x - (link.getLabelBounds().w + 20);
            }
 
            link.setLabelPosition(new DvtDiagramPoint(linkLabelXPos, linkLabelYPos));
        }
    },
    /**
     * Utility function to return the size information for
     * the largest node being handled by the layout
     */
    getMaxNodeBounds : function (layoutContext) {
        var nodeCount = layoutContext.getNodeCount();
        var maxW = 0;
        var maxH = 0;
        for (var i = 0;i < nodeCount;i++) {
            var node = layoutContext.getNodeByIndex(i);
            var bounds = node.getContentBounds();
            maxW = Math.max(bounds.w, maxW);
            maxH = Math.max(bounds.h, maxH);
        }
        return new DvtDiagramRectangle(0, 0, maxW, maxH);
    },
    /**
     * Utility function to return the size information for
     * the largest link label being handled by the layout
     */
 
    getMaxLinkLabelBounds : function (layoutContext) {
        var linkCount = layoutContext.getLinkCount();
        var maxW = 0;
        var maxH = 0;
        for (var i = 0;i < linkCount;i++) {
            var link = layoutContext.getLinkByIndex(i);
            var bounds = link.getLabelBounds();
            maxW = Math.max(bounds.w, maxW);
            maxH = Math.max(bounds.h, maxH);
        }
        return new DvtDiagramRectangle(0, 0, maxW, maxH);
    },
    /**
     * Utility function to return the midpoint of a node edge.  
     * Edges are identified as compass points n e s w
     */
    getNodeEdgeMidPoint : function (node, edge) {
        var nodeSize = node.getContentBounds();
        var nodePosition = node.getPosition();
        var xpos;
        var ypos;
 
        switch (edge) {
            case "n":
            case "s":
                xpos = nodePosition.x + (nodeSize.w / 2);
                break;
            case "e":
                xpos = nodePosition.x + nodeSize.w;
                break;
            case "w":
                xpos = nodePosition.x;
                break;
            default :
                xpos = 0;
        }
 
        switch (edge) {
            case "e":
            case "w":
                ypos = nodePosition.y + (nodeSize.h / 2);
                break;
            case "s":
                ypos = nodePosition.y + nodeSize.h;
                break;
            case "n":
                ypos = nodePosition.y;
                break;
            default :
                ypos = 0;
        }
 
        return new DvtDiagramPoint(xpos, ypos);
    }
}

F.4.3 Code Sample for Simple Vertical Layout

This diagram layout uses a restricted vertical layout with the assumption of no more than one incoming and one outgoing link per node.

var LearningDiagramLayouts = {
    simpleVertical : function (layoutContext) {
        var largestNodeBounds = LearningDiagramLayouts.getMaxNodeBounds(layoutContext);
        var nodeGap = (2 * largestNodeBounds.h);
        var nodeCount = layoutContext.getNodeCount();
        var linkCount = layoutContext.getLinkCount();
        var xPos = 0;
        var yPos = 0;
        var labelXPos = largestNodeBounds.w + 10;
 
        for (var i = 0;i < nodeCount;i++) {
            var node = layoutContext.getNodeByIndex(i);
            var nodeHeight = node.getContentBounds().h;
            node.setPosition(new DvtDiagramPoint(xPos, yPos));
 
            var labelHeight = node.getLabelBounds().h;
            var labelYPos = yPos + ((nodeHeight - labelHeight) / 2);
 
            node.setLabelPosition(new DvtDiagramPoint(labelXPos, labelYPos));
            yPos += (nodeHeight + nodeGap);
        }
 
        var linkGap = 4;
        var largestLinkLabelBounds = LearningDiagramLayouts.getMaxLinkLabelBounds(layoutContext);
        for (var j = 0;j < linkCount;j++) {
            var link = layoutContext.getLinkByIndex(j);
            var startNode = layoutContext.getNodeById(link.getStartId());
            var endNode = layoutContext.getNodeById(link.getEndId());
            var linkStartPos = LearningDiagramLayouts.getNodeEdgeMidPoint(startNode, "s");
            var linkEndPos = LearningDiagramLayouts.getNodeEdgeMidPoint(endNode, "n");
            
            var linkLabelXPos;
            var linkLabelYPos = linkStartPos.y + ((linkEndPos.y - linkStartPos.y - link.getLabelBounds().h) / 2);
 
            if (linkEndPos.y > linkStartPos.y) {
                link.setPoints([linkStartPos.x, (linkStartPos.y + linkGap), linkEndPos.x, (linkEndPos.y - linkGap)]);
                linkLabelXPos = linkStartPos.x - (link.getLabelBounds().w + 20);
            } else {
                // need to re-route in this case 
                // Segment 1 - keep heading down for a short distance 
                var turn1 = new DvtDiagramPoint();
                turn1.x = linkStartPos.x;
                turn1.y = linkStartPos.y + (largestNodeBounds.h/2);
                // Segment 2 - head left far enough to avoid overlap with the other link labels
                var turn2 = new DvtDiagramPoint();
                turn2.x = turn1.x - (largestLinkLabelBounds.w + 40);
                turn2.y = turn1.y;
                // Segment 3 - Back up the diagram to a point equally above the end node
                var turn3 = new DvtDiagramPoint();
                turn3.x = turn2.x;
                turn3.y = linkEndPos.y - (largestNodeBounds.h/2);
                // Segment 4 - Back to the center line
                var turn4 = new DvtDiagramPoint();
                turn4.x = linkEndPos.x;
                turn4.y = turn3.y;
                // Segment 5 - And down to the end node for which 
                // we already have the coordinates
                
                //Now pass in the path:
                link.setPoints([linkStartPos.x, (linkStartPos.y + linkGap), 
                                                 turn1.x, turn1.y,
                                                 turn2.x, turn2.y,
                                                 turn3.x, turn3.y,
                                                 turn4.x, turn4.y,
                                                 linkEndPos.x, (linkEndPos.y - linkGap)]);
                                                 
               //Finally work out the X position of the label in this case
                linkLabelXPos = turn3.x - (link.getLabelBounds().w + 20);
            }
 
            link.setLabelPosition(new DvtDiagramPoint(linkLabelXPos, linkLabelYPos));
        }
    },
    /**
     * Utility function to return the size information for
     * the largest node being handled by the layout
     */
    getMaxNodeBounds : function (layoutContext) {
        var nodeCount = layoutContext.getNodeCount();
        var maxW = 0;
        var maxH = 0;
        for (var i = 0;i < nodeCount;i++) {
            var node = layoutContext.getNodeByIndex(i);
            var bounds = node.getContentBounds();
            maxW = Math.max(bounds.w, maxW);
            maxH = Math.max(bounds.h, maxH);
        }
        return new DvtDiagramRectangle(0, 0, maxW, maxH);
    },
    /**
     * Utility function to return the size information for
     * the largest link label being handled by the layout
     */
 
    getMaxLinkLabelBounds : function (layoutContext) {
        var linkCount = layoutContext.getLinkCount();
        var maxW = 0;
        var maxH = 0;
        for (var i = 0;i < linkCount;i++) {
            var link = layoutContext.getLinkByIndex(i);
            var bounds = link.getLabelBounds();
            maxW = Math.max(bounds.w, maxW);
            maxH = Math.max(bounds.h, maxH);
        }
        return new DvtDiagramRectangle(0, 0, maxW, maxH);
    },
    /**
     * Utility function to return the midpoint of a node edge.  
     * Edges are identified as compass points n e s w
     */
    getNodeEdgeMidPoint : function (node, edge) {
        var nodeSize = node.getContentBounds();
        var nodePosition = node.getPosition();
        var xpos;
        var ypos;
 
        switch (edge) {
            case "n":
            case "s":
                xpos = nodePosition.x + (nodeSize.w / 2);
                break;
            case "e":
                xpos = nodePosition.x + nodeSize.w;
                break;
            case "w":
                xpos = nodePosition.x;
                break;
            default :
                xpos = 0;
        }
 
        switch (edge) {
            case "e":
            case "w":
                ypos = nodePosition.y + (nodeSize.h / 2);
                break;
            case "s":
                ypos = nodePosition.y + nodeSize.h;
                break;
            case "n":
                ypos = nodePosition.y;
                break;
            default :
                ypos = 0;
        }
 
        return new DvtDiagramPoint(xpos, ypos);
    }
}