この付録では、このガイド全体の項で参照されている、完全な長さのコード・サンプルを示します。
この付録には次の項が含まれます:
次に、ADF Facesアーキテクチャを使用する場合のコード例を示します。
デフォルトのADF Facesのadf-js-partitions.xml
ファイルには、独自のパーティション・ファイルを作成してオーバーライドできるパーティションがあります。詳細は、第4.9項「JavaScriptライブラリのパーティション化」を参照してください。例F-1に、デフォルトのADF Facesのadf-js-partitions.xml
ファイルを示します。
例F-1 デフォルトのadf-js-partitions.xmlファイル
<?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>
次のサンプル・コードは、Employee
クラス、サンプル・データおよび23.7.6項「グラフへのドラッグ・アンド・ドロップの追加」で参照されるドラッグ・アンド・ドロップ・メソッドを作成します。
例F-2 グラフのドラッグ・アンド・ドロップのサンプルのdragAndDropマネージドBean
public class dragAndDrop { private Random m_random = new Random(23); // Simple class to represent employee data public static class Employee { public final String name; private int performance; private int potential; private double salary; private double bonus; private int experience; public Employee(String name, Random r) { this.name = name; this.performance = r.nextInt(100); this.potential = r.nextInt(100); this.salary = r.nextInt(100000); this.bonus = r.nextInt(50000); this.experience = r.nextInt(20); } // Returns a copy of the employee with a new name public Employee(String name, Employee e) { this.name = name; this.performance = e.performance; this.potential = e.potential; this.salary = e.salary; this.bonus = e.bonus; this.experience = e.experience; } public String getName() {return name;} public int getPerformance() { return performance; } public int getPotential() { return potential; } public void setSalary(double salary) { this.salary = salary; } public double getSalary() { return salary; } public void setBonus(double bonus) { this.bonus = bonus; } public double getBonus() { return bonus; } public int getExperience() { return experience; } } // Returns the employee represented by the selection private Employee findEmployee(List<Employee> list, GraphSelection selection) { if(!(selection instanceof DataSelection)) return null; List<KeyMap> groupKeys = ((DataSelection)selection).getGroupKeys(); for(KeyMap groupKey : groupKeys) { Set groupKeySet = groupKey.keySet(); for(Object key : groupKeySet) { // Find the employee String name = groupKey.get((String)key); Employee emp = findEmployee(list, name); if(emp != null) { return emp; } } } return null; } private Employee findEmployee(List<Employee> list, String name) { for(Employee emp : list) { if(name.equals(emp.name)) return emp; } return null; } // Converts the list of employees to a bubble data model private static DataModel createBubbleDataModel(List<Employee> employees) { Object[][] data = new Object[employees.size()*3][1]; Object[] colLabels = new Object[employees.size()*3]; Object[] rowLabels = {"Employees"}; for(int i=0; i<employees.size(); i++) { Employee emp = employees.get(i); data[i*3][0] = emp.performance; data[i*3+1][0] = emp.salary; data[i*3+2][0] = emp.experience; colLabels[i*3] = emp.name; colLabels[i*3+1] = emp.name + "1"; colLabels[i*3+2] = emp.name + "2"; } LocalXMLDataSource dataSource = new LocalXMLDataSource(colLabels, rowLabels, data); return new GraphDataModel(dataSource); } protected List<Employee> m_graphList = this.setEmployeeList(); public List<Employee> setEmployeeList(){ List<Employee> em_graphList = new ArrayList<Employee>(); em_graphList.add(new Employee("Dan", m_random)); em_graphList.add(new Employee("Ben", m_random)); em_graphList.add(new Employee("Dave", m_random)); em_graphList.add(new Employee("Chris", m_random)); em_graphList.add(new Employee("Frank", m_random)); em_graphList.add(new Employee("Jill", m_random)); em_graphList.add(new Employee("Ray", m_random)); return em_graphList; } public DataModel getGraphModel() { if(m_graphList == null) { setEmployeeList(); } return createBubbleDataModel(m_graphList); } protected List<Employee> m_tableModel = this.setTableModelList(); public List<Employee> setTableModelList(){ List<Employee> em_tableModel = new ArrayList<Employee>(); em_tableModel.add(new Employee("Brad", m_random)); em_tableModel.add(new Employee("Derrick", m_random)); em_tableModel.add(new Employee("Matt", m_random)); return em_tableModel; } public List getTableModel() { if(m_tableModel == null) { setTableModelList(); } return m_tableModel; } public DnDAction fromGraphDropListener(DropEvent event) { // Get the ComponentHandle from the transferable Transferable transferable = event.getTransferable(); GraphSelectionSet selectionSet = transferable.getData(GraphSelectionSet.class); // Now change each marker based on the DropEvent's proposed action DnDAction proposedAction = event.getProposedAction(); for(GraphSelection selection : selectionSet) { Employee emp = findEmployee(m_graphList, selection); if(emp == null) return DnDAction.NONE; if(proposedAction == DnDAction.COPY) { m_tableModel.add(new Employee("Copy of " + emp.getName(), emp)); } else if(proposedAction == DnDAction.LINK) { m_tableModel.add(new Employee("Link to " + emp.getName(), emp)); } else if(proposedAction == DnDAction.MOVE) { m_graphList.remove(emp); m_tableModel.add(emp); } } RequestContext.getCurrentInstance().addPartialTarget(event.getDragComponent()); return proposedAction; } public DnDAction fromTableDropListener(DropEvent event) { Transferable transferable = event.getTransferable(); DataFlavor<RowKeySet> dataFlavor = DataFlavor.getDataFlavor(RowKeySet.class, "fromTable"); RowKeySet set = transferable.getData(dataFlavor); Employee emp = null; if(set != null && !set.isEmpty()) { int index = (Integer) set.iterator().next(); emp = m_tableModel.get(index); } if(emp == null) return DnDAction.NONE; DnDAction proposedAction = event.getProposedAction(); if(proposedAction == DnDAction.COPY) { m_graphList.add(emp); } else if(proposedAction == DnDAction.LINK) { m_graphList.add(emp); } else if(proposedAction == DnDAction.MOVE) { m_graphList.add(emp); m_tableModel.remove(emp); } else return DnDAction.NONE; RequestContext.getCurrentInstance().addPartialTarget(event.getDragComponent()); return event.getProposedAction(); }
次は、ツリーマップとサンバースト・コンポーネントを作成するサンプル・コードです。
ツリーマップあるいはサンバーストをUI優先開発を使用して作成するには、クラスとマネージドBeanを使用してツリー・ノードとツリー・モデルを定義し、ツリーにデータを移入し、かつツリーマップあるいはサンバースト構成の必要に応じ、追加のメソッドを追加します。
例F-3に、国勢調査のデータ例でのツリー・ノードを定義するコードのサンプルを示します。ラベル、サイズ、および色についての必要な設定は、パラメータとしてツリー・ノードに渡されます。
例F-3 ツリーマップあるいはサンバーストのツリー・ノードを作成するコード・サンプル
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()); } }
UI優先開発におけるツリーマップあるいはサンバーストにデータを供給するには、クラスあるいはマネージドBeanをユーザーのアプリケーションに追加します。アプリケーションは例F-3のツリー・ノードを展開し、そこにデータを移入します。ツリー・モデルを設定するクラスは、org.apache.myfaces.trinidad.model.TreeModel
クラスの実装にする必要があります。ツリー・モデルを定義したら、org.apache.myfaces.trinidad.model.ChildPropertyTreeModel
を実装するメソッドを作成してツリー・モデルを完成させます。
例F-4は、国勢データの例において、ルートと子ノードの構造を設定し、子レベルにデータを移入し、ノードの色およびサイズを定義するサンプル・クラスを示します。
例F-4 ツリーマップおよびサンバースト用に国勢調査データ・モデルを作成するコード・サンプル
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; } } }
最後に、UI-first開発におけるツリー・モデルを完成するには、マネージドBeanを、データを含むクラスまたはBeanを参照するユーザーのアプリケーションに加え、オプションとして、ツリーマップあるいはサンバーストのカスタマイズのために他のメソッドを追加します。
例F-5に、国勢調査のツリーマップをインスタンス化し、それに国勢調査データを移入するコードのサンプルを示します。この例はまた、ツリーマップのノードの行データをラベル表示のための文字列に変換するサンプル・メソッド(convertToString
)を含んでいます。
例F-5 国勢調査データのツリーマップ設定のためのマネージドBeanの例
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; } }
両方のコンポーネントは同じツリー・モデルを使用するため、サンバースト国勢調査の例を設定するコードはほとんど同一です。例は、F.3.2項「サンバースト・マネージドBeanのサンプル・コード」を参照してください。
次のサンプル・コードは、国勢調査サンバーストをインスタンス化し、それに国勢調査データを移入します。この例はまた、サンバーストのノードの行データをラベル表示のための文字列に変換するサンプル・メソッド(convertToString
)を含んでいます。
例F-6 国勢調査データのサンバースト設定のためのマネージドBeanの例
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; } }
次に、カスタム・コンポーネントを作成するコード例を示します。
カスタム・コンポーネントの作成時には、マウス・クリックなどのイベントが起動された場合に必要な機能を実行するコードをJavaScriptで記述する必要があります。例F-7に、tagPane
コンポーネントに追加できるイベント・コードを示します。
例F-7 tagPaneイベントのJavaScript
/** * Fires a select type event to the server for the source component * when a tag is clicked. */ function AcmeTagSelectEvent(source, tag) { AdfAssert.assertPrototype(source, AdfUIComponent); AdfAssert.assertString(tag); this.Init(source, tag); } // make AcmeTagSelectEvent a subclass of AdfComponentEvent AdfObject.createSubclass(AcmeTagSelectEvent, AdfComponentEvent); /** * The event type */ AcmeTagSelectEvent.SELECT_EVENT_TYPE = "tagSelect"; /** * Event Object constructor */ AcmeTagSelectEvent.prototype.Init = function(source, tag) { AdfAssert.assertPrototype(source, AdfUIComponent); AdfAssert.assertString(tag); this._tag = tag; AcmeTagSelectEvent.superclass.Init.call(this, source, AcmeTagSelectEvent.SELECT_EVENT_TYPE);} /** * Indicates this event should be sent to the server */ AcmeTagSelectEvent.prototype.propagatesToServer = function() { return true; } /** * Override of AddMarshalledProperties to add parameters * sent server side. */ AcmeTagSelectEvent.prototype.AddMarshalledProperties = function( properties) { properties.tag = this._tag; } /** * Convenient method for queue a AcmeTagSelectEvent. */ AcmeTagSelectEvent.queue = function(component, tag) { AdfAssert.assertPrototype(component, AdfUIComponent); AdfAssert.assertString(tag); AdfLogger.LOGGER.logMessage(AdfLogger.FINEST, "AcmeTagSelectEvent.queue(component, tag)"); new AcmeTagSelectEvent(component, tag).queue(true); } /** * returns the selected file type */ AcmeTagSelectEvent.prototype.getTag = function() { return this._tag;} /** * returns a debug string */ AcmeTagSelectEvent.prototype.toDebugString = function() { var superString = AcmeTagSelectEvent.superclass.toDebugString.call(this); return superString.substring(0, superString.length - 1) + ", tag=" + this._tag + "]"; } /* * * Make sure that this event only invokes immediate validators * on the client. */ AcmeTagSelectEvent.prototype.isImmediate = function() { return true; }
カスタム・コンポーネントの作成時には、Javaクラスの詳細をJSPコンパイル・エンジンとIDEツールに提供する、タグ・ライブラリ・ディスクリプタ(TLD)ファイルを作成する必要があります。例F-8に、tagPane
コンポーネントを定義するTLDファイルの例を示します。
例F-8 tagPaneのacme.tldタグ・ライブラリ・ディスクリプタ・コード
<?xml version = '1.0' encoding = 'windows-1252'?> <taglib xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd" version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"> <description>Acme Corporation JSF components</description> <display-name>acme</display-name> <tlib-version>1.0</tlib-version> <short-name>acme</short-name> <uri>http://oracle.adfdemo.acme</uri> <tag> <description> </description> <name>tagPane</name> <tag-class>oracle.adfdemo.acme.faces.taglib.TagPaneTag</tag-class> <body-content>JSP</body-content> <attribute> <name>id</name> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>rendered</name> <deferred-value> <type>boolean</type> </deferred-value> </attribute> <attribute> <name>tagSelectListener</name> <deferred-method> <method-signature>void </method-signature> myMethod(oracle.adfdemo.acme.faces.event.TagSelectEvent) </deferred-method> </attribute> <attribute> <name>visible</name> <deferred-value> <type>boolean</type> </deferred-value> </attribute> <attribute> <name>partialTriggers</name> <deferred-value> </deferred-value> </attribute> <attribute> <name>inlineStyle</name> <deferred-value/> </attribute> <attribute> <name>inlineClass</name> <deferred-value/> </attribute> <attribute> <name>tags</name> <deferred-value/> </attribute> <attribute> <name>binding</name> <deferred-value/> </attribute> <attribute> <name>orderBy</name> <deferred-value/> </attribute> </tag> </taglib>
カスタム・コンポーネントを作成する場合は、プロパティ情報とアクセッサを含むJavaクラスを作成する必要があります。例F-9に、サンプルのTagPaneコンポーネントの作成に使用するコードを示します。
例F-9 TagPaneコンポーネント・クラス
package oracle.adfdemo.acme.faces.component; import java.util.Map; import java.util.Set; import javax.el.MethodExpression; import javax.faces.event.AbortProcessingException; import javax.faces.event.FacesEvent; import oracle.adf.view.rich.event.ClientListenerSet; import oracle.adfdemo.acme.faces.event.TagSelectEvent; import oracle.adfdemo.acme.faces.event.TagSelectListener; import org.apache.myfaces.trinidad.bean.FacesBean; import org.apache.myfaces.trinidad.bean.PropertyKey; import org.apache.myfaces.trinidad.component.UIXObject; public class TagPane extends UIXObject { /** * <p>Establishes a <code>Type</code> bean that contains JSF component property * information. This static public final property <code>TYPE</code> shadows a * property with the same name in the super class. The <code>TYPE</code> * attribute is defined once pre JSF component class. The factory method call to * <code>FacesBean.Type</code> passes the super class reference * <code>UIXObject.TYPE</code> which copies down property information defined in * the super type.</p> */ static public final FacesBean.Type TYPE = new FacesBean.Type(UIXObject.TYPE); /** * <p>Custom CSS applied to the style attribute of the root markup node.</p> */ static public final PropertyKey INLINE_STYLE_KEY = TYPE.registerKey("inlineStyle", String.class); /** * <p>Custom CSS class to the class attribute of the root markup node.</p> */ static public final PropertyKey STYLE_CLASS_KEY = TYPE.registerKey("styleClass", String.class); /** * <p>Key used to identify the <code>visible</code> property that * is of type <code>Boolean.class</code>.</p> */ static public final PropertyKey VISIBLE_KEY = TYPE.registerKey("visible", Boolean.class, Boolean.TRUE); /** * <p>Key used to identify the <code>partialTriggers</code> property that * is of type <code>String[].class</code>.</p> */ static public final PropertyKey PARTIAL_TRIGGERS_KEY = TYPE.registerKey("partialTriggers", String[].class); /** * <p>Key used to identify the <code>tagSelectListener</code> property that * is of type <code>MethodExpression.class</code>.</p> */ static public final PropertyKey TAG_SELECT_LISTENER_KEY = TYPE.registerKey("tagSelectListener", MethodExpression.class); /** * <p>Key used to identity the <code>clientComponent</code> flag that * is of type <code>Boolean.class</code>.</p> */ static public final PropertyKey CLIENT_COMPONENT_KEY = TYPE.registerKey("clientComponent", Boolean.class); /** * <p>Key use to identity the <code>clientListeners</code> property * that is a collection class of <code>ClientListenerSet.class</code>.</p> */ static public final PropertyKey CLIENT_LISTENERS_KEY = TYPE.registerKey("clientListeners", ClientListenerSet.class, PropertyKey.CAP_NOT_BOUND); /** * <p>Key used to identify the <code>clientAttributes</code> property this * is a set of type <code>PropertyKey.CAP_NOT_BOUND</code>.</p> */ static public final PropertyKey CLIENT_ATTRIBUTES_KEY = TYPE.registerKey("clientAttributes", Set.class, PropertyKey.CAP_NOT_BOUND); /** * <p>Key used to identify the <code>tags</code> property that * is of type <code>Map.class</code>.</p> */ static public final PropertyKey TAGS_KEY = TYPE.registerKey("tags", Map.class); /** * <p>Key used to identify the <code>orderBy</code> property that * is of type <code>String.class</code>.</p> */ static public final PropertyKey ORDER_BY_KEY = TYPE.registerKey("orderBy", String.class); /** * <p>The constructor calls the super classes overloaded constructor passing * the <code>rendererType</code>. */ public TagPane() { // Pass the renderer type to the super class. super(RENDERER_TYPE); // requires a client component by default setBooleanProperty(CLIENT_COMPONENT_KEY, Boolean.TRUE); } /** * @param newpartialTriggers array of render dependent client ids */ public void setPartialTriggers(String[] newpartialTriggers) { setProperty(PARTIAL_TRIGGERS_KEY, newpartialTriggers); } /** * @return array of render dependent client ids */ public String[] getPartialTriggers() { return (String[]) getProperty(PARTIAL_TRIGGERS_KEY); } /** * <p>Sets client listeners that provide specialized behaviors such as * drag-and-drop. * You can subscribe to any client event using the event type and method. * For example, we could add a client listener for our custom client event * <code>oracle.adfdemo.acme.js.event.TagSelectEvent</code>. * </p> * <pre> * <acme:tagPane tags="#{bean.tags}"> * <clientListener type="tagSelect" method="onClickType" /> * <\acme:tagPane> * </pre> * * <p>In the fragment above, the method "onClickType" is a JavaScript function that * has a single event parameter.</p> * * <per> * function onClickType(event) { * alter("You clicked on tag: " + event.getTag()); * } * </per> * * @param newclientListeners client listener collection */ public void setClientListeners(ClientListenerSet newclientListeners) { setProperty(CLIENT_LISTENERS_KEY, newclientListeners); } /** * <p>Returns client listeners that provide specialized behaviors such as * drag-and-drop.</p> * * @return client listener set */ public ClientListenerSet getClientListeners() { return (ClientListenerSet) getProperty(CLIENT_LISTENERS_KEY); } /** * <p>Sets attributes on the client component, * <code>oracle.adfdemo.acme.js.event.AcmeTagPane.js</code> that can be used by * <code>ClientListners</code>. The state is actually managed by the * component. This collection is a <code>Set</code> of client attribute names * that are also server component properties. * <br/><br/> * Client attributes must be set using the <code>setClientAttribute</code> JSP tag. * Consider the following: * <pre> * <acme:tagPane targetFolder="somefolder"> * <clientListener type="tagSelect" method="onClickType" /> * <clientAttribute name="whoareyou" method="While E. Coyote" /> * <\acme:tagPane> * </pre> * * <p>In the fragment above, the method "onClickType" is a JavaScript function that * has a single event parameter.</p> * * <per> * function onClickType(event) { * alter(event.getSource().getProperty("whoareyou") * + " clicked on tag: " + event.getTag()); * } * </per> * * @param newclientAttributes a set of client attribute names */ public void setClientAttributes(Set newclientAttributes) { setProperty(CLIENT_ATTRIBUTES_KEY, newclientAttributes); } /** * <p><code>Set</code> of client component attribute names. The state * is also kept in the server side component's <code>attributes</code> Map.</p> * * @return colleciton of unique client component attribute names */ public Set getClientAttributes() { return (Set) getProperty(CLIENT_ATTRIBUTES_KEY); } /** * <p>Sets the <code>newselectListener</code> method binding * expression that expects a single parameter of type {@link * oracle.adfdemo.acme.faces.event.TagSelectEvent}. * This binding will be when the client-side * <code>oracle.adfdemo.acme.js.event.AcmeTagSelectEvent.js</code> * is queued from clicking on one of the <code>tag</code>s.</p> * * @param newselectListener invokes a {@link oracle.adfdemo.acme.faces.event.TagSelectEvent} method expression */ public void setTagSelectListener(MethodExpression newselectListener) { setProperty(TAG_SELECT_LISTENER_KEY, newselectListener); } /** * <p>Returns a method binding expression that is fired when the client-side * <code>oracle.adfdemo.acme.js.event.AcmeTagSelectEvent.js</code> is * queued.</p> * * @return invokes a {@link oracle.adfdemo.acme.faces.event.TagSelectEvent} * method expression */ public MethodExpression getTagSelectListener() { return (MethodExpression) getProperty(TAG_SELECT_LISTENER_KEY); } /** * <p>Method for adding a listener implementing {@link * oracle.adfdemo.acme.faces.event.TagSelectListener}.</p> * * @param listener tag select listener */ public void addTagSelectListener(TagSelectListener listener) { addFacesListener(listener); } /** * <p>Removes a {@link oracle.adfdemo.acme.faces.event.TagSelectListener} * listener.</p> * * @param listener tag select listener */ final public void removeSelectListener(TagSelectListener listener) { removeFacesListener(listener); } /** * <p>Returns an array of attached {@link * oracle.adfdemo.acme.faces.event.TagSelectListener} listeners.</p> * * @return an array of attached select listeners. */ final public TagSelectListener[] getSelectListeners() { return (TagSelectListener[]) getFacesListeners(TagSelectListener.class); } /** * <p>Exposes the <code>FacesBean.Type</code> for this class through a protected * method. This method is called but the <code>UIComponentBase</code> super * class to setup the components <code>ValueMap</code> which is the container * for the <code>attributes</code> colleciton.</p> * * @return <code>FolderSummary.TYPE</code> static property */ @Override protected FacesBean.Type getBeanType() { return TYPE; } /** * <p> * </p> * * @param facesEvent faces event * @throws AbortProcessingException exception during processing */ @Override public void broadcast(FacesEvent facesEvent) throws AbortProcessingException { // notify the bound TagSelectListener if (facesEvent instanceof TagSelectEvent) { TagSelectEvent event = (TagSelectEvent) facesEvent; // utility method found in UIXComponentBase for invoking method event // expressions broadcastToMethodExpression(event, getTagSelectListener()); } super.broadcast(facesEvent); } /** * <p>CSS value applied to the root component's style attribute.</p> * * @param newinlineStyle CSS custom style text */ public void setInlineStyle(String newinlineStyle) { // inlineStyle = newinlineStyle; setProperty(INLINE_STYLE_KEY, newinlineStyle); } /** * <p>CSS value applied to the root component's style attribute.</p> * * @return newinlineStyle CSS custom style text */ public String getInlineStyle() { // return inlineStyle; return (String) getProperty(INLINE_STYLE_KEY); } /** * <p>CSS style class added to the components class attribute.</p> * @param newstyleClass custom class style attribute */ public void setStyleClass(String newstyleClass) { setProperty(STYLE_CLASS_KEY, newstyleClass); } /** * <p>CSS style class added to the components class attribute.</p> * @return newstyleClass custom class style attribute */ public String getStyleClass() { return (String) getProperty(STYLE_CLASS_KEY); } /** * <p>Sets the visibility of the component. The visibility * is not the same as <code>rendered</code>. The <code>visible</code> * attribute effects the CSS style on the CSS root of the component. * </p> * * @param newvisible <code>true</code> if the markup is not hidden in the * browser */ public void setVisible(boolean newvisible) { setBooleanProperty(VISIBLE_KEY, newvisible); } /** * <p>Returns the visibility of the component. The visibility * is not the same as <code>rendered</code>. The <code>visible</code> * attribute effects the CSS style on the CSS root of the component. * </p> * * @return <code>true</code> if the markup is not hidden in the browser */ public boolean isVisible() { return getBooleanProperty(VISIBLE_KEY, Boolean.TRUE); } /** * <p>This component's type, <code>oracle.adfdemo.acme.TagPane</code></p> */ static public final String COMPONENT_TYPE = "oracle.adfdemo.acme.TagPane"; /** * <p>Logical name given to the registered renderer for this component.</p> */ static public final String RENDERER_TYPE = "oracle.adfdemo.acme.TagPane"; /** * <p>Sets a Map of weighted tags. The key represents the tag name * and the value a number.</p> * * @param newtags tags to be rendered */ public void setTags(Map<String, Number> newtags) { setProperty(TAGS_KEY, newtags); } /** * <p>Gets a Map of weighted tags. The key represents the tag name * and the value a number.</p> * * @return newtags tags to be rendered */ public Map<String, Number> getTags() { return (Map<String, Number>) getProperty(TAGS_KEY); } /** * <p>Sets the order that the tags are rendered. * The valid enumerations are "alpha" and "weight".</p> * * @param neworderBy order by enumeration */ public void setOrderBy(String neworderBy) { setProperty(ORDER_BY_KEY, neworderBy); } /** * <p>Gets the order that the tags are rendered. * The valid enumerations are "alpha" and "weight".</p> * * @return order by enumeration */ public String getOrderBy() { return (String) getProperty(ORDER_BY_KEY); } static { // register the new TYPE by family and rendererType TYPE.lockAndRegister(UIXObject.COMPONENT_FAMILY, RENDERER_TYPE); } }
レンダラ・クラスはコンポーネントの表示を制御します。例F-10に、カスタムTagPaneコンポーネントのレンダラ・クラスを示します。
例F-10 レンダラ・クラス
package oracle.adfdemo.acme.faces.render; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.logging.Logger; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import oracle.adf.view.rich.render.ClientComponent; import oracle.adf.view.rich.render.ClientEvent; import oracle.adf.view.rich.render.ClientMetadata; import oracle.adf.view.rich.render.RichRenderer; import oracle.adfdemo.acme.faces.component.TagPane; import oracle.adfdemo.acme.faces.event.TagSelectEvent; import org.apache.myfaces.trinidad.bean.FacesBean; import org.apache.myfaces.trinidad.bean.PropertyKey; import org.apache.myfaces.trinidad.context.RenderingContext; /** * <p>Renderer for {@link oracle.adfdemo.acme.faces.component.TagPane} * component.</p> */ public class TagPaneRenderer extends RichRenderer { /** * <p>Java Logger instance.</p> */ private static final Logger _LOG = Logger.getLogger(TagPaneRenderer.class.getName()); /** * <p>No-args constructor passes the <code>TagPane.TYPE</code> to the * base class. The type bean is used to extend the component properties * meta-data by invoking <code>findTypeConstants</code>.</p> */ public TagPaneRenderer() { super(TagPane.TYPE); } /** * <p>Invoked by the base class, this is the hook to perform the markup * rendering for the {@link oracle.adfdemo.acme.faces.component.TagPane}. * </p> * * @param context faces context * @param arc rendering context * @param component {@link oracle.adfdemo.acme.faces.component.TagPane} * @param client server-side state for the client component * @param bean state holder for the TagPane component * @throws IOException error rendering markup */ protected void encodeAll(FacesContext context, RenderingContext arc, UIComponent component, ClientComponent client, FacesBean bean) throws IOException { //defines this attribute to be pushed to the client component //this attribute is not secured so its value will prorogate //to the server when modified from the client client.addProperty(_ORDER_BY_KEY, bean.getProperty(_ORDER_BY_KEY)); ResponseWriter writer = context.getResponseWriter(); writer.startElement("div", component); // write the client id for the component renderId(context, component); // add the component root style attribute; factors in the inline and class // style component properties renderRootStyleAttributes(context, arc, client, bean, getDefaultStyleClass(context, arc, bean)); writer.startElement("span", component); // writes the style class attribute using a skinning key; the key // will need to be normalized and compressed renderStyleClass(context, arc, TagPaneRenderer._TAG_CONTENT_STYLE_KEY); _renderTags(context, component, arc, bean); writer.endElement("span"); writer.endElement("div"); } /** * <p>Renders the <code>tags</code> as anchored links ordered by * tag or tag weight. The tag weight is converted into a font that is * evenly distributed between the <code>MAXIMUM_FONT_SIZE</code> and the * <code>MINIMUM_FONT_SIZE</code> based on the tag's weight.</p> * * @param context faces context * @param component {@link oracle.adfdemo.acme.faces.component.TagPane} * @param arc rendering context * @param bean state holder for {@link oracle.adfdemo.acme.faces.component.TagPane} * @throws IOException error during rendering tags */ private void _renderTags(FacesContext context, UIComponent component, RenderingContext arc, FacesBean bean) throws IOException { Map<String, Number> tags = _getTags(bean); if (tags.isEmpty()) { return; // no work to do } // Calculates a font size for each tag. The font size will be based on the // weight of the tag and the size proportional to the range of font sizes. Map<String, Integer> fontSizes = _computeFontSize(tags, _getMaximumFontSize(arc), _getMinimumFontSize(arc)); // create a list from the map's entry set. List<Map.Entry<String, Number>> entries = new ArrayList<Map.Entry<String, Number>>(tags.entrySet()); //collocation sequence determined by the orderBy property Collections.sort(entries, _getComparator(bean)); ResponseWriter writer = context.getResponseWriter(); //for each tag, write an anchored tag. The font size is explicitly set //using a inline style. for (Map.Entry<String, Number> entry: entries) { // start a hyperlink for each tag writer.startElement("a", component); // render the class style for the link renderStyleClass(context, arc, TagPaneRenderer._TAG_STYLE_KEY); writer.writeAttribute("href", "#", null); //font size is calculated from the _computeFontSize call above //the _getFontSizeStyle substitutes the font size parameter into the style String style = _getFontSizeStyle(fontSizes.get(entry.getKey())); //set the font size using an inline style writer.writeAttribute("style", style, null); //insert the tag weight into the title string String title = _getTitleStyle(entry.getValue().doubleValue(), arc); // write the title string attribute to the hyperlink writer.writeAttribute("title", title, null); //write body of the tag; the description of the link will //be the name of the tag writer.writeText(entry.getKey(), null); //end the hyperlink writer.endElement("a"); // write a small spacer between anchored tags renderSpacer(context, arc, "2px", "2px"); } } /** * <p>Substitutes the tag's <code>weight</code> into the pattern defined by * the <code>AcmeTagPane_tag_title</code> resource key.</p> * @param weight tag weight * @param arc rendering context * @return formatted title attribute for the anchored tag */ private String _getTitleStyle(double weight, RenderingContext arc) { // title attribute template from the resource bundle MessageFormat titleFormat = new MessageFormat(arc.getTranslatedString("AcmeTagPane_tag_title")); return titleFormat.format(new Object[] { weight }); } /** * <p>Substitutes the <code>size</code> into the style pattern defined by * the <code>_FONT_INLINESTYLE_FORMAT</code>.</p> * @param size font size * @return inline style defining the font size */ private String _getFontSizeStyle(int size) { return _FONT_INLINESTYLE_FORMAT.format(new Object[] { size }); } /** * <p>Returns the default constructor for our client-side component, * <code>oracle.adfdemo.acme.js.component.AcmeTagPane.js</code></p> * * @return client-side component name */ protected String getClientConstructor() { return _CLIENT_COMPONENT; } /** * <p>Retrieves a hint on if and when a component must be sent down to the * client.</p> * * @return the client component type */ @Override protected ClientComponent.Type getDefaultClientComponentType() { return ClientComponent.Type.CREATE_WITH_REQUIRED_ATTRS; } /** * <p>Retrieves the style class to apply to the root element, * <code>_ROOT_STYLE_KEY</code>.</p> * * @param context the FacesContext * @param arc the RenderingContext * @param bean the FacesBean of the component to render * @return the root element's style class */ @Override protected String getDefaultStyleClass(FacesContext context, RenderingContext arc, FacesBean bean) { return TagPaneRenderer._ROOT_STYLE_KEY; } /** * <p>Returns the weighted tag map from the component's state holder * properties bag using the <code>_TAGS_KEY</code>.</p> * * @param bean state holder for {@link * oracle.adfdemo.acme.faces.component.TagPane} * @return map of weighted tags */ private Map<String, Number> _getTags(FacesBean bean) { Map<String, Number> tags = (Map<String, Number>) bean.getProperty(_TAGS_KEY); if (tags == null) { tags = Collections.emptyMap(); } return tags; } /** * <p>Looks at the {@link oracle.adfdemo.acme.faces.component.TagPane}'s * <code>orderBy</code> property and returns a corresponding * <code>Comparator</code>. The default is to sort descending by weight. * If <code>orderBy</code> equals "alpha", the order is sorted ascending * by the tag.</p> * * @param bean state holder for the {@link *oracle.adfdemo.acme.faces.component.TagPane} * @return comparator to sort the <code>tags</code> */ private Comparator<Map.Entry<String, Number>> _getComparator(FacesBean bean) { String orderBy = (String) bean.getProperty(_ORDER_BY_KEY); if (orderBy != null && orderBy.equalsIgnoreCase("alpha")) { return _sortAscByTag; } return this._sortDescByWeight; } /** * <p>This method is called from the <code>RichRenderer</code>'s implementation * of the <code>decode</code> method. Within this method we will check to see * if there are any custom client events, * <code>oracle.adfdemo.acme.js.event.AcmeTagSelectEvent</code>. * If a client event is present, we will queue a corresponding server-side faces * event, {@link oracle.adfdemo.acme.faces.event.TagSelectEvent}. This event is * targeted for the apply request values phase so the response will be completed * if a client event is found. * </p> * * @param facesContext faces context * @param component {@link oracle.adfdemo.acme.faces.component.TagPane} * component * @param clientId unique component identifier */ @Override public void decodeInternal(FacesContext facesContext, UIComponent component, String clientId) { super.decodeInternal(facesContext, component, clientId); // look for a client event of type "tagSelect" ClientEvent clientEvent = this.getClientEvent(facesContext, clientId, _CLIENT_SELECT_EVENT_TYPE); if (clientEvent != null) { // extract the tag from the client event String tag = (String) clientEvent.getParameters().get(TagPaneRenderer._CLIENT_SELECT_EVENT_TAG_PARAM); _LOG.finest("Found SelectEvent with tag of " + (tag != null? tag: "null")); // Instantiate a corresponding server side event TagSelectEvent event = new TagSelectEvent(component, tag); // push the event up to the view root event.queue(); // this event is an assumed immediate meaning that we will stop the //lifecycle after // the apply request values phase and short-circuit to render response. // there doesn't seem to be a good reason for this component to participate // in the process validations phase. that is why we are going to jump to // the render response phase. facesContext.renderResponse(); } } /** * <p>This method is called from the base constructor. The callback gives * opportunity to add client characteristics to the server-side component * properties that become client-side component properties.</p> * * @param type <code>FacesBean.TYPE</code> * @param metadata client specific extension to the <code>FacesBean.Type</code> */ @Override protected void findTypeConstants(FacesBean.Type type, ClientMetadata metadata) { super.findTypeConstants(type, metadata); _TAGS_KEY = type.findKey("tags"); metadata.addSecureProperty(_TAGS_KEY); _ORDER_BY_KEY = type.findKey("orderBy"); } /** * <p>Calculates a proportional font weight for each of the <code>tags</code> * based on the weight of the tag entry. The weight of each entry is * determined by the value of the <code>Map.Entry</code>. The computed font * weight for each item will be between the <code>maxFontSize</code> and * <code>minFontSize</code>. Tags that have the same weight will be * displayed using the same font.</p> * * @param tags weighted tags * @param maxFontSize maximum font weight * @param minFontSize minimum font weight * @return Lookup map that contains the tag name as the key and the value * is the computed font size. */ private Map<String, Integer> _computeFontSize(Map<String, Number> tags, double maxFontSize, double minFontSize) { double maxWeight = 0; // max weight double minWeight = 0; // min weight // find the min and max in the tag weight set for (Map.Entry<String, Number> entry: tags.entrySet()) { // first time set the min to the first entry if (minWeight == 0) { minWeight = entry.getValue().doubleValue(); } maxWeight = Math.max(maxWeight, entry.getValue().doubleValue()); minWeight = Math.min(minWeight, entry.getValue().doubleValue()); } //target map where the key is the tag and the value the font size Map<String, Integer> fontSizeMap = new TreeMap<String, Integer>(); // for each entry, calculate a font size adding to the target // map by tag name for (Map.Entry<String, Number> entry: tags.entrySet()) { double weight = entry.getValue().doubleValue(); double percentTotaWeight = (weight - (minWeight - 1d)) / ((maxWeight + 1d)); double fontSize = (percentTotaWeight * ((maxFontSize + 1d) - minFontSize)) + (minFontSize - 1d); //add to the xref map fontSizeMap.put(entry.getKey(), (int) Math.round(fontSize)); } return fontSizeMap; } /** * <p>Returns the minimum font size that will be used as the lower limit when * assigning each unique tag weight a font size. The value will be pulled from * the skinning key "acme|tagPane-tr-minimum-font-size". If the * key is not found or the value is not an integer, the * <code>_DEFAULT_MINIMUM_FONT_SIZE</code> will be used.</p> * * @param arc Rendering context * @return max font size assigned to a tag */ private int _getMinimumFontSize(RenderingContext arc) { int size = _DEFAULT_MINIMUM_FONT_SIZE; String fontSize = (String) arc.getSkin().getProperty(_MINIMUM_FONT_SIZE_PROPERTY); if (fontSize != null) { try { size = Integer.parseInt(fontSize); } catch (NumberFormatException e) { _LOG.warning("The \"" + _MINIMUM_FONT_SIZE_PROPERTY + "\" skin property " + "is not defined or is not a valid integer. " + "Using default minimum font size: " + size); } } return size; } /** * <p>Returns the maximum font size that will be used as the upper limit when * assigning each unique tag weight a font size. The value will be pulled from * the skinning key "acme|tagPane-tr-maximum-font-size". If the * key is not found or the value is not an integer, the * <code>_DEFAULT_MAXIMUM_FONT_SIZE</code> will be used.</p> * * @param arc Rendering context * @return max font size assigned to a tag */ private int _getMaximumFontSize(RenderingContext arc) { int size = _DEFAULT_MAXIMUM_FONT_SIZE; String fontSize = (String) arc.getSkin().getProperty(_MAXIMUM_FONT_SIZE_PROPERTY); if (fontSize != null) { try { size = Integer.parseInt(fontSize); } catch (NumberFormatException e) { _LOG.warning("The \"" + _MAXIMUM_FONT_SIZE_PROPERTY + "\" skin property " + "is not defined or is not a valid integer. " + "Using default maximum font size: " + size); } } return size; } /** * <p>Sorts the <code>tags.entrySet</code> by map value in descending order * and by map key in ascending. * </p> */ private Comparator<Map.Entry<String, Number>> _sortDescByWeight = new Comparator<Map.Entry<String, Number>>() { public int compare(Map.Entry<String, Number> o1, Map.Entry<String, Number> o2) { double value1 = o1.getValue().doubleValue(); double value2 = o2.getValue().doubleValue(); if (value1 > value2) { return -1; } else if (value1 < value2) { return 1; } else { // sort by value DESC, key ASC return o1.getKey().compareTo(o2.getKey()); } } }; /** * <p>Sorts the <code>tags.entrySet</code> by the key in ascending order. * </p> */ private Comparator<Map.Entry<String, Number>> _sortAscByTag = new Comparator<Map.Entry<String, Number>>() { public int compare(Map.Entry<String, Number> o1, Map.Entry<String, Number> o2) { return o1.getKey().compareTo(o2.getKey()); } }; /** * <p>Default maximum font size for rendered tags (20).</p> */ private static final int _DEFAULT_MAXIMUM_FONT_SIZE = 20; /** * <p>Minimum font size for rendered tags (2).</p> */ private static final int _DEFAULT_MINIMUM_FONT_SIZE = 8; /** * <p>The client event type for * <code>oracle.adfdemo.acme.js.event.AcmeTagSelectEvent.js</code>. * This constant will correspond to <code>AcmeTagSelectEvent.SELECT_EVENT_ * TYPE</code>.</p> */ private static final String _CLIENT_SELECT_EVENT_TYPE = "tagSelect"; /** * <p>The tag parameter name on the * <code>oracle.asfdemo.acme.js.event.AcmeTagSelectEvent.js</code>. * The tag reports one of the weighted tags the user has click on. The * <code>AddMarshalledProperties</code> function is where this parameter is * prepared to be sent to the server where it is intercepted by the * <code>oracle.adfinternal.view.faces.context.RichPhaseListener</code> and * unmarshaled into a <code>oracle.adf.view.rich.render.ClientEvent</code>. * </p> * */ private static final String _CLIENT_SELECT_EVENT_TAG_PARAM = "tag"; /** * <p>The root skinning key <code>acme|tagPane</code>.</p> */ private static final String _ROOT_STYLE_KEY = "acme|tagPane"; /** * <p>The maximum font size skinning key * <code>acme|tagPane-tr-maximum-font-size</code>.</p> */ private static final String _MAXIMUM_FONT_SIZE_PROPERTY = _ROOT_STYLE_KEY + "-tr-maximum-font-size"; /** * <p>The minimum font size skinning key * <code>acme|tagPane-tr-minimum-font-size</code>.</p> */ private static final String _MINIMUM_FONT_SIZE_PROPERTY = _ROOT_STYLE_KEY + "-tr-minimum-font-size"; /** * <p>TagPane content skinning key <code>acme|tagPane::content</code>.</p> */ private static final String _TAG_CONTENT_STYLE_KEY = "acme|tagPane::content"; /** * <p>Tag link skinning key <code>acme|tagPane::tag</code>.</p> */ private static final String _TAG_STYLE_KEY = "acme|tagPane::tag"; /** * <p>The <code>tags</code> property key from the <code>TagPane.TYPE</code>.</p> */ private static PropertyKey _TAGS_KEY = null; /** * <p>The <code>orderBy</code> property key from the * <code>TagPane.TYPE</code>.</p> */ private static PropertyKey _ORDER_BY_KEY = null; /** * <p>The Corresponding JavaScript component, * <code>oracle.adfdemo.acme.js.component.AcmeTagPane.js</code>.</p> */ private static final String _CLIENT_COMPONENT = "AcmeTagPane"; /** * <p>Pattern used to build the inline style that specifies the font * size for a tag. Pixels units are assumed.</p> */ private static final MessageFormat _FONT_INLINESTYLE_FORMAT = new MessageFormat("font-size:{0}px;"); }