Highfacesを拡張して、グラフの縦軸を反転させる

# グラフの軸を反転させたい
Highfacesでとあるランキングの折れ線グラフを作成する際、ランキングなのでy軸の最大値を0にする必要があります。ただ、HighChartsにはある「reversed」（軸を反転させるオプション）がHighfacesにはなかったので、カスタムタグを作成して「reversed」を指定できるようにします。

# タグハンドラとコンポーネントを拡張する
以下のタグハンドラと、グラフとグラフの軸用のコンポーネントを拡張します。
このとき、「reversed」という属性を指定できるように、ChartAxisExクラスにアクセサを追記しています。

```java:ChartHandler.java
public class ChartHandlerEx extends ChartHandler {
	public ChartHandlerEx(ComponentConfig config) {
		super(config);
	}
}
```
```java:ChartHandler.java
@FacesComponent("hogehoge.component.ChartEx")
public class ChartEx extends Chart {
}
```
```java:ChartAxis.java
@FacesComponent("hogehoge.component.ChartAxisEx")
public class ChartAxisEx extends ChartAxis {
	public Object getReversed() {
		return Object.class.cast(this.getStateHelper().eval("reversed", null));
	}

	public void setReversed(Object value) {
		this.getStateHelper().put("reversed", value);
	}
}
```

# レンダラを拡張する
「reversed」オプションを出力するように、ChartRendererクラスを拡張し、「encodeAxes」というメソッドに以下を追記します。

```java:ChartRendererEx.java
@FacesRenderer(componentFamily = "org.highfaces.component", rendererType = "hogehoge.renderer,ChartRendererEx")
@ListenerFor(systemEventClass = PostAddToViewEvent.class)
public class ChartRendererEx extends ChartRenderer {

    @Override
    public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
    // 省略
    }

    private void encodeAxes(FacesContext context, Chart cv, JSONObject high) throws JSONException, IOException {
    // 省略
    if (axis.getReversed() != null) {
        j.put("reversed", axis.getReversed());
    }
}
```
# taglib.xmlを作成する
src/main/resources/META-INFにtaglib.xmlを作成して、上記で拡張したクラスを指定します。

```xml:highfaces.taglib.xml
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee"
	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-facelettaglibrary_2_0.xsd"
	version="2.0">
	<namespace>http://hogehoge.jp/taglib</namespace>
	<tag>
		<tag-name>chartEx</tag-name>
		<component>
			<component-type>hogehoge.component.ChartEx</component-type>
			<renderer-type>hogehoge.renderer.ChartRendererEx</renderer-type>
			<handler-class>hogehoge.component.ChartHandlerEx</handler-class>
		</component>
		<attribute>
			<description><![CDATA[Type of the charts, possible values are line, spline, area, column, bar and pie]]></description>
			<name>type</name>
			<required>true</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[List of HTML colors for the chart series. Defaults to "#2f7ed8,#0d233a,#8bbc21,#910000,#1aadce,#492970,#f28f43,#77a1e5,#c42525,#a6c96a".]]></description>
			<name>colors</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Datasource of the component, can be a Collection or Map type. Provide either a value, a model or chartseries as children declared in the xhtml file.]]></description>
			<name>value</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Datasource of the component, must implement the ChartModel interface. Provide either a value, a model or chartseries as children declared in the xhtml file.]]></description>
			<name>model</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[ValueExpression of the currently selected Series.]]></description>
			<name>selectedSeries</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[ValueExpression of the currently selected Point inside the selected Series. This corresponds to the category/value of the x axis.]]></description>
			<name>selectedPoint</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[JavaScript to be executed on point selection.]]></description>
			<name>onselect</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Height of chart in px]]></description>
			<name>height</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Width of chart in px]]></description>
			<name>width</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Title for the chart]]></description>
			<name>title</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Credits text for the chart]]></description>
			<name>credits</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Zoom type text for the chart]]></description>
			<name>zoomType</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Sub Title for the chart]]></description>
			<name>subTitle</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Label for x-axis]]></description>
			<name>xaxisLabel</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Label for y-axis]]></description>
			<name>yaxisLabel</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Name of the request scoped variable used as iterator for value]]></description>
			<name>var</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Index of currently rendered item during iteration over the Collection in value-Attribute]]></description>
			<name>rowIndexVar</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[ValueExpression of the value of a point, bar,... Only valid if used in combinatino with value.]]></description>
			<name>point</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[ValueExpression of the tickLabel for a single point. Only valid if used in combinatino with value.]]></description>
			<name>tickLabel</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[ID of rerender component for ajax actions]]></description>
			<name>render</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[JavaScript function name for client side customization. All highcharts configuration options are available inside the function using 'this'.]]></description>
			<name>extender</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[Whether to stack the values of each series on top of each other. Possible values are 'none', 'normal' or 'percent', defaulting to 'none'.]]></description>
			<name>stacking</name>
			<required>false</required>
			<type>java.lang.String</type>
		</attribute>
		<attribute>
			<description><![CDATA[If set, this is the time interval in seconds that the updateMethod will be called and series data will change.]]></description>
			<name>updateInterval</name>
			<required>false</required>
			<type>java.lang.Integer</type>
		</attribute>
		<attribute>
			<description><![CDATA[How many points should be always visible in the chart in liveUpdate mode ? Defaults to 20.]]></description>
			<name>updateCapacity</name>
			<required>false</required>
			<type>java.lang.Integer</type>
		</attribute>
		<attribute>
			<description><![CDATA[This method will be called via AJAX each time, the chart needs to update the visible data. Use it to change or add data to the displayed series.]]></description>
			<name>updateMethod</name>
			<required>false</required>
			<type>javax.el.MethodExpression</type>
		</attribute>
		<attribute>
			<description><![CDATA[JavaScript to be executed on mouse over event.]]></description>
			<name>onmouseover</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[JavaScript to be executed on mouse over event.]]></description>
			<name>onmouseoverpoint</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
	</tag>
	<tag>
		<tag-name>chartAxisEx</tag-name>
		<component>
			<component-type>hogehoge.component.ChartAxisEx</component-type>
		</component>
		<attribute>
			<description><![CDATA[Position of this axis. Possible values are n, w, s or e.]]></description>
			<name>position</name>
			<required>true</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Angle in degrees if the tickLabel should be rotated. Defaults to 0.]]></description>
			<name>tickAngle</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Color of the gridline for this axis within the chart.]]></description>
			<name>gridLineColor</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Width of the gridline for this axis within the chart. Use 0 to not display a gridline.]]></description>
			<name>gridLineWidth</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Type of this axis. Possible values are linear, category and datetime, defaulting to linear.]]></description>
			<name>type</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Label-Style of this axis. Use CSS definition style to e.g. define a color.]]></description>
			<name>style</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Title of this axis.]]></description>
			<name>title</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Label-Format of this axis. To display e.g. mm use "{value} mm"]]></description>
			<name>format</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Minimum value for this axis]]></description>
			<name>min</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Maximum value for this axis]]></description>
			<name>max</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
		<attribute>
			<description><![CDATA[Reversed for this axis]]></description>
			<name>reversed</name>
			<required>false</required>
			<type>java.lang.Object</type>
		</attribute>
	</tag>
</facelet-taglib>
```
# 実際に使ってみる
カスタムタグができたので、「chartAxisEx」タグで「reversed」が指定できることを確認します。

```xhtml:hogehoge.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:hf="http://highfaces.org"
	xmlns:ex="http://hogehoge.jp/taglib">
<h:head>
	<title>hogehogeグラフ</title>
</h:head>
<h:body>
	<ex:chartEx type="line" title="hogehogeグラフ">
		<hf:chartSerie name="何かの順位"
			value="#{hogehogeBean}"
			var="rank" point="#{rank.rank}" tickLabel="#{rank.time}">
		</hf:chartSerie>
		<ex:chartAxisEx position="w" min="1" max="100" reversed="true"
			title="" />
		<ex:chartAxisEx position="s" title="" type="datetime"
			format="{value:%Y.%m.%d}" />
	</ex:chartEx>
</h:body>
</html>
```
# 完了
以上で、Highfacesを拡張して、グラフの軸が反転できるようになりました。

BEFORE:
![WS000009.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/c1202c35-b2c6-6109-6836-76139e9e42f2.jpeg)

AFTER:
![WS000008.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/da881fa5-e116-7d13-4052-846db13d2778.jpeg)