Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.chart; 027 028 029import java.util.*; 030import javafx.animation.*; 031import javafx.beans.property.DoubleProperty; 032import javafx.collections.FXCollections; 033import javafx.collections.ObservableList; 034import javafx.event.ActionEvent; 035import javafx.event.EventHandler; 036import javafx.geometry.Orientation; 037import javafx.scene.Node; 038import javafx.scene.layout.StackPane; 039import javafx.util.Duration; 040import com.sun.javafx.charts.Legend; 041import javafx.css.StyleableDoubleProperty; 042import javafx.css.CssMetaData; 043import javafx.css.PseudoClass; 044import com.sun.javafx.css.converters.SizeConverter; 045import javafx.collections.ListChangeListener; 046import javafx.css.Styleable; 047import javafx.css.StyleableProperty; 048 049 050/** 051 * StackedBarChart is a variation of {@link BarChart} that plots bars indicating 052 * data values for a category. The bars can be vertical or horizontal depending 053 * on which axis is a category axis. 054 * The bar for each series is stacked on top of the previous series. 055 */ 056public class StackedBarChart<X, Y> extends XYChart<X, Y> { 057 058 // -------------- PRIVATE FIELDS ------------------------------------------- 059 private Map<Series, Map<String, List<Data<X, Y>>>> seriesCategoryMap = new HashMap<Series, Map<String, List<Data<X, Y>>>>(); 060 private Legend legend = new Legend(); 061 private final Orientation orientation; 062 private CategoryAxis categoryAxis; 063 private ValueAxis valueAxis; 064 private int seriesDefaultColorIndex = 0; 065 private Map<Series<X, Y>, String> seriesDefaultColorMap = new HashMap<Series<X, Y>, String>(); 066 // RT-23125 handling data removal when a category is removed. 067 private ListChangeListener<String> categoriesListener = new ListChangeListener<String>() { 068 @Override public void onChanged(ListChangeListener.Change<? extends String> c) { 069 while (c.next()) { 070 for(String cat : c.getRemoved()) { 071 for (Series<X,Y> series : getData()) { 072 for (Data<X, Y> data : series.getData()) { 073 if ((cat).equals((orientation == orientation.VERTICAL) ? 074 data.getXValue() : data.getYValue())) { 075 boolean animatedOn = getAnimated(); 076 setAnimated(false); 077 dataItemRemoved(data, series); 078 setAnimated(animatedOn); 079 } 080 } 081 } 082 requestChartLayout(); 083 } 084 } 085 } 086 }; 087 088 // -------------- PUBLIC PROPERTIES ---------------------------------------- 089 /** The gap to leave between bars in separate categories */ 090 private DoubleProperty categoryGap = new StyleableDoubleProperty(10) { 091 @Override protected void invalidated() { 092 get(); 093 requestChartLayout(); 094 } 095 096 @Override 097 public Object getBean() { 098 return StackedBarChart.this; 099 } 100 101 @Override 102 public String getName() { 103 return "categoryGap"; 104 } 105 106 public CssMetaData<StackedBarChart<?,?>,Number> getCssMetaData() { 107 return StackedBarChart.StyleableProperties.CATEGORY_GAP; 108 } 109 }; 110 111 public double getCategoryGap() { 112 return categoryGap.getValue(); 113 } 114 115 public void setCategoryGap(double value) { 116 categoryGap.setValue(value); 117 } 118 119 public DoubleProperty categoryGapProperty() { 120 return categoryGap; 121 } 122 123 // -------------- CONSTRUCTOR ---------------------------------------------- 124 /** 125 * Construct a new StackedBarChart with the given axis. The two axis should be a ValueAxis/NumberAxis and a CategoryAxis, 126 * they can be in either order depending on if you want a horizontal or vertical bar chart. 127 * 128 * @param xAxis The x axis to use 129 * @param yAxis The y axis to use 130 */ 131 public StackedBarChart(Axis<X> xAxis, Axis<Y> yAxis) { 132 this(xAxis, yAxis, FXCollections.<Series<X, Y>>observableArrayList()); 133 } 134 135 /** 136 * Construct a new StackedBarChart with the given axis and data. The two axis should be a ValueAxis/NumberAxis and a 137 * CategoryAxis, they can be in either order depending on if you want a horizontal or vertical bar chart. 138 * 139 * @param xAxis The x axis to use 140 * @param yAxis The y axis to use 141 * @param data The data to use, this is the actual list used so any changes to it will be reflected in the chart 142 */ 143 public StackedBarChart(Axis<X> xAxis, Axis<Y> yAxis, ObservableList<Series<X, Y>> data) { 144 super(xAxis, yAxis); 145 getStyleClass().add("stacked-bar-chart"); 146 setLegend(legend); 147 if (!((xAxis instanceof ValueAxis && yAxis instanceof CategoryAxis) 148 || (yAxis instanceof ValueAxis && xAxis instanceof CategoryAxis))) { 149 throw new IllegalArgumentException("Axis type incorrect, one of X,Y should be CategoryAxis and the other NumberAxis"); 150 } 151 if (xAxis instanceof CategoryAxis) { 152 categoryAxis = (CategoryAxis) xAxis; 153 valueAxis = (ValueAxis) yAxis; 154 orientation = Orientation.VERTICAL; 155 } else { 156 categoryAxis = (CategoryAxis) yAxis; 157 valueAxis = (ValueAxis) xAxis; 158 orientation = Orientation.HORIZONTAL; 159 } 160 // update css 161 pseudoClassStateChanged(HORIZONTAL_PSEUDOCLASS_STATE, orientation == Orientation.HORIZONTAL); 162 pseudoClassStateChanged(VERTICAL_PSEUDOCLASS_STATE, orientation == Orientation.VERTICAL); 163 setData(data); 164 categoryAxis.getCategories().addListener(categoriesListener); 165 } 166 167 /** 168 * Construct a new StackedBarChart with the given axis and data. The two axis should be a ValueAxis/NumberAxis and a 169 * CategoryAxis, they can be in either order depending on if you want a horizontal or vertical bar chart. 170 * 171 * @param xAxis The x axis to use 172 * @param yAxis The y axis to use 173 * @param data The data to use, this is the actual list used so any changes to it will be reflected in the chart 174 * @param categoryGap The gap to leave between bars in separate categories 175 */ 176 public StackedBarChart(Axis<X> xAxis, Axis<Y> yAxis, ObservableList<Series<X, Y>> data, double categoryGap) { 177 this(xAxis, yAxis); 178 setData(data); 179 setCategoryGap(categoryGap); 180 } 181 182 // -------------- METHODS -------------------------------------------------- 183 @Override protected void dataItemAdded(Series<X, Y> series, int itemIndex, Data<X, Y> item) { 184 String category; 185 if (orientation == Orientation.VERTICAL) { 186 category = (String) item.getXValue(); 187 } else { 188 category = (String) item.getYValue(); 189 } 190 // Don't plot if category does not already exist ? 191// if (!categoryAxis.getCategories().contains(category)) return; 192 193 Map<String, List<Data<X, Y>>> categoryMap = seriesCategoryMap.get(series); 194 195 if (categoryMap == null) { 196 categoryMap = new HashMap<String, List<Data<X, Y>>>(); 197 seriesCategoryMap.put(series, categoryMap); 198 } 199 // list to hold more that one bar "positive and negative" 200 List<Data<X, Y>> itemList = categoryMap.get(category) != null ? categoryMap.get(category) : new ArrayList<Data<X, Y>>(); 201 itemList.add(item); 202 categoryMap.put(category, itemList); 203// categoryMap.put(category, item); 204 Node bar = createBar(series, getData().indexOf(series), item, itemIndex); 205 if (shouldAnimate()) { 206 animateDataAdd(item, bar); 207 } else { 208 getPlotChildren().add(bar); 209 } 210 } 211 212 @Override protected void dataItemRemoved(final Data<X, Y> item, final Series<X, Y> series) { 213 final Node bar = item.getNode(); 214 if (shouldAnimate()) { 215 Timeline t = createDataRemoveTimeline(item, bar, series); 216 t.setOnFinished(new EventHandler<ActionEvent>() { 217 218 public void handle(ActionEvent event) { 219 removeDataItemFromDisplay(series, item); 220 } 221 }); 222 t.play(); 223 } else { 224 getPlotChildren().remove(bar); 225 removeDataItemFromDisplay(series, item); 226 } 227 } 228 229 /** @inheritDoc */ 230 @Override protected void dataItemChanged(Data<X, Y> item) { 231 double barVal; 232 double currentVal; 233 if (orientation == Orientation.VERTICAL) { 234 barVal = ((Number) item.getYValue()).doubleValue(); 235 currentVal = ((Number) getCurrentDisplayedYValue(item)).doubleValue(); 236 } else { 237 barVal = ((Number) item.getXValue()).doubleValue(); 238 currentVal = ((Number) getCurrentDisplayedXValue(item)).doubleValue(); 239 } 240 if (currentVal > 0 && barVal < 0) { // going from positive to negative 241 // add style class negative 242 item.getNode().getStyleClass().add("negative"); 243 } else if (currentVal < 0 && barVal > 0) { // going from negative to positive 244 // remove style class negative 245 item.getNode().getStyleClass().remove("negative"); 246 } 247 } 248 249 private void animateDataAdd(Data<X, Y> item, Node bar) { 250 double barVal; 251 if (orientation == Orientation.VERTICAL) { 252 barVal = ((Number) item.getYValue()).doubleValue(); 253 if (barVal < 0) { 254 bar.getStyleClass().add("negative"); 255 } 256 item.setYValue(getYAxis().toRealValue(getYAxis().getZeroPosition())); 257 setCurrentDisplayedYValue(item, getYAxis().toRealValue(getYAxis().getZeroPosition())); 258 getPlotChildren().add(bar); 259 item.setYValue(getYAxis().toRealValue(barVal)); 260 animate(new Timeline( 261 new KeyFrame(Duration.ZERO, new KeyValue(currentDisplayedYValueProperty(item), getCurrentDisplayedYValue(item))), 262 new KeyFrame(Duration.millis(700), new KeyValue(currentDisplayedYValueProperty(item), item.getYValue(), Interpolator.EASE_BOTH))) 263 ); 264 } else { 265 barVal = ((Number) item.getXValue()).doubleValue(); 266 if (barVal < 0) { 267 bar.getStyleClass().add("negative"); 268 } 269 item.setXValue(getXAxis().toRealValue(getXAxis().getZeroPosition())); 270 setCurrentDisplayedXValue(item, getXAxis().toRealValue(getXAxis().getZeroPosition())); 271 getPlotChildren().add(bar); 272 item.setXValue(getXAxis().toRealValue(barVal)); 273 animate(new Timeline( 274 new KeyFrame(Duration.ZERO, new KeyValue(currentDisplayedXValueProperty(item), getCurrentDisplayedXValue(item))), 275 new KeyFrame(Duration.millis(700), new KeyValue(currentDisplayedXValueProperty(item), item.getXValue(), Interpolator.EASE_BOTH))) 276 ); 277 } 278 } 279 280 /** @inheritDoc */ 281 @Override protected void seriesAdded(Series<X, Y> series, int seriesIndex) { 282 String defaultColorStyleClass = "default-color" + (seriesDefaultColorIndex % 8); 283 seriesDefaultColorMap.put(series, defaultColorStyleClass); 284 seriesDefaultColorIndex++; 285 // handle any data already in series 286 // create entry in the map 287 Map<String, List<Data<X, Y>>> categoryMap = new HashMap<String, List<Data<X, Y>>>(); 288 for (int j = 0; j < series.getData().size(); j++) { 289 Data<X, Y> item = series.getData().get(j); 290 Node bar = createBar(series, seriesIndex, item, j); 291 String category; 292 if (orientation == Orientation.VERTICAL) { 293 category = (String) item.getXValue(); 294 } else { 295 category = (String) item.getYValue(); 296 } 297 // list of two item positive and negative 298 List<Data<X, Y>> itemList = categoryMap.get(category) != null ? categoryMap.get(category) : new ArrayList<Data<X, Y>>(); 299 itemList.add(item); 300 categoryMap.put(category, itemList); 301 if (shouldAnimate()) { 302 animateDataAdd(item, bar); 303 } else { 304 double barVal = (orientation == Orientation.VERTICAL) ? ((Number)item.getYValue()).doubleValue() : 305 ((Number)item.getXValue()).doubleValue(); 306 if (barVal < 0) { 307 bar.getStyleClass().add("negative"); 308 } 309 getPlotChildren().add(bar); 310 } 311 } 312 if (categoryMap.size() > 0) { 313 seriesCategoryMap.put(series, categoryMap); 314 } 315 } 316 317 private Timeline createDataRemoveTimeline(Data<X, Y> item, final Node bar, final Series<X, Y> series) { 318 Timeline t = new Timeline(); 319 if (orientation == Orientation.VERTICAL) { 320 item.setYValue(getYAxis().toRealValue(getYAxis().getZeroPosition())); 321 t.getKeyFrames().addAll( 322 new KeyFrame(Duration.ZERO, new KeyValue(currentDisplayedYValueProperty(item), 323 getCurrentDisplayedYValue(item))), 324 new KeyFrame(Duration.millis(700), new EventHandler<ActionEvent>() { 325 @Override public void handle(ActionEvent actionEvent) { 326 getPlotChildren().remove(bar); 327 } 328 }, 329 new KeyValue(currentDisplayedYValueProperty(item), item.getYValue(), Interpolator.EASE_BOTH))); 330 } else { 331 item.setXValue(getXAxis().toRealValue(getXAxis().getZeroPosition())); 332 t.getKeyFrames().addAll( 333 new KeyFrame(Duration.ZERO, new KeyValue(currentDisplayedXValueProperty(item), 334 getCurrentDisplayedXValue(item))), 335 new KeyFrame(Duration.millis(700), new EventHandler<ActionEvent>() { 336 @Override public void handle(ActionEvent actionEvent) { 337 getPlotChildren().remove(bar); 338 } 339 }, 340 new KeyValue(currentDisplayedXValueProperty(item), item.getXValue(), Interpolator.EASE_BOTH))); 341 } 342 return t; 343 } 344 345 @Override protected void seriesRemoved(final Series<X, Y> series) { 346 // remove all symbol nodes 347 if (shouldAnimate()) { 348 ParallelTransition pt = new ParallelTransition(); 349 pt.setOnFinished(new EventHandler<ActionEvent>() { 350 351 public void handle(ActionEvent event) { 352 removeSeriesFromDisplay(series); 353 requestChartLayout(); 354 } 355 }); 356 for (Data<X, Y> d : series.getData()) { 357 final Node bar = d.getNode(); 358 // Animate series deletion 359 if (getSeriesSize() > 1) { 360 for (int j = 0; j < series.getData().size(); j++) { 361 Data<X, Y> item = series.getData().get(j); 362 Timeline t = createDataRemoveTimeline(item, bar, series); 363 pt.getChildren().add(t); 364 } 365 } else { 366 // fade out last series 367 FadeTransition ft = new FadeTransition(Duration.millis(700), bar); 368 ft.setFromValue(1); 369 ft.setToValue(0); 370 ft.setOnFinished(new EventHandler<ActionEvent>() { 371 372 @Override 373 public void handle(ActionEvent actionEvent) { 374 getPlotChildren().remove(bar); 375 } 376 }); 377 pt.getChildren().add(ft); 378 } 379 } 380 pt.play(); 381 } else { 382 for (Data<X, Y> d : series.getData()) { 383 final Node bar = d.getNode(); 384 getPlotChildren().remove(bar); 385 } 386 removeSeriesFromDisplay(series); 387 requestChartLayout(); 388 } 389 } 390 391 /** @inheritDoc */ 392 @Override protected void updateAxisRange() { 393 // This override is necessary to update axis range based on cumulative Y value for the 394 // Y axis instead of the inherited way where the max value in the data range is used. 395 final Axis<X> xa = getXAxis(); 396 final Axis<Y> ya = getYAxis(); 397 if (xa.isAutoRanging()) { 398 List xData = new ArrayList<Number>(); 399 if (xa instanceof CategoryAxis) { 400 xData.addAll(categoryAxis.getCategories()); 401 } else { 402 int catIndex = 0; 403 for (String category : categoryAxis.getCategories()) { 404 int index = 0; 405 double totalXN = 0; 406 double totalXP = 0; 407 Iterator<Series<X, Y>> seriesIterator = getDisplayedSeriesIterator(); 408 while (seriesIterator.hasNext()) { 409 Series<X, Y> series = seriesIterator.next(); 410 for (final Data<X, Y> item : getDataItem(series, index, catIndex, category)) {; 411 if (item != null) { 412 boolean isNegative = item.getNode().getStyleClass().contains("negative"); 413 if (!isNegative) { 414 totalXP += xa.toNumericValue(item.getXValue()); 415 } else { 416 totalXN += xa.toNumericValue(item.getXValue()); 417 } 418 } 419 } 420 } 421 xData.add(totalXP); 422 xData.add(totalXN); 423 catIndex++; 424 } 425 } 426 xa.invalidateRange(xData); 427 } 428 if (ya.isAutoRanging()) { 429 List yData = new ArrayList<Number>(); 430 if (ya instanceof CategoryAxis) { 431 yData.addAll(categoryAxis.getCategories()); 432 } else { 433 int catIndex = 0; 434 for (String category : categoryAxis.getCategories()) { 435 int index = 0; 436 double totalYP = 0; 437 double totalYN = 0; 438 Iterator<Series<X, Y>> seriesIterator = getDisplayedSeriesIterator(); 439 while (seriesIterator.hasNext()) { 440 Series<X, Y> series = seriesIterator.next(); 441 for (final Data<X, Y> item : getDataItem(series, index, catIndex, category)) {; 442 if(item != null) { 443 boolean isNegative = item.getNode().getStyleClass().contains("negative"); 444 if (!isNegative) { 445 totalYP += ya.toNumericValue(item.getYValue()); 446 } else { 447 totalYN += ya.toNumericValue(item.getYValue()); 448 } 449 } 450 } 451 } 452 yData.add(totalYP); 453 yData.add(totalYN); 454 catIndex++; 455 } 456 } 457 ya.invalidateRange(yData); 458 } 459 } 460 461 /** @inheritDoc */ 462 @Override protected void layoutPlotChildren() { 463 double catSpace = categoryAxis.getCategorySpacing(); 464 // calculate bar spacing 465 final double availableBarSpace = catSpace - getCategoryGap(); 466 final double barWidth = availableBarSpace; 467 final double barOffset = -((catSpace - getCategoryGap()) / 2); 468 final double zeroPos = valueAxis.getZeroPosition(); 469 // update bar positions and sizes 470 int catIndex = 0; 471 for (String category : categoryAxis.getCategories()) { 472 int index = 0; 473 int currentPositiveHeight = 0; 474 int currentNegativeHeight = 0; 475 Iterator<Series<X, Y>> seriesIterator = getDisplayedSeriesIterator(); 476 while (seriesIterator.hasNext()) { 477 Series<X, Y> series = seriesIterator.next(); 478 for (final Data<X, Y> item : getDataItem(series, index, catIndex, category)) {; 479 if (item != null) { 480 final Node bar = item.getNode(); 481 final double categoryPos; 482 final double valPos; 483 if (orientation == Orientation.VERTICAL) { 484 categoryPos = getXAxis().getDisplayPosition(getCurrentDisplayedXValue(item)); 485 valPos = getYAxis().getDisplayPosition(getCurrentDisplayedYValue(item)); 486 } else { 487 categoryPos = getYAxis().getDisplayPosition(getCurrentDisplayedYValue(item)); 488 valPos = getXAxis().getDisplayPosition(getCurrentDisplayedXValue(item)); 489 } 490 final double bottom; 491 final double top; 492 boolean isNegative = bar.getStyleClass().contains("negative"); 493 if (!isNegative) { 494 bottom = currentPositiveHeight + Math.min(valPos, zeroPos); 495 top = currentPositiveHeight + Math.max(valPos, zeroPos); 496 if (orientation == Orientation.VERTICAL) { 497 currentPositiveHeight -= top - bottom; 498 } else { 499 currentPositiveHeight += top - bottom; 500 } 501 } else { 502 bottom = currentNegativeHeight + Math.min(valPos, zeroPos); 503 top = currentNegativeHeight + Math.max(valPos, zeroPos); 504 if (orientation == Orientation.VERTICAL) { 505 currentNegativeHeight += top - bottom; 506 } else { 507 currentNegativeHeight += top - bottom; 508 } 509 } 510 if (orientation == Orientation.VERTICAL) { 511 bar.resizeRelocate(categoryPos + barOffset, 512 bottom, barWidth, top - bottom); 513 } else { 514 //noinspection SuspiciousNameCombination 515 bar.resizeRelocate(bottom, 516 categoryPos + barOffset, 517 top - bottom, barWidth); 518 } 519 index++; 520 } 521 } 522 } 523 catIndex++; 524 } 525 } 526 527 /** 528 * Computes the size of series linked list 529 * @return size of series linked list 530 */ 531 @Override int getSeriesSize() { 532 int count = 0; 533 Iterator<Series<X, Y>> seriesIterator = getDisplayedSeriesIterator(); 534 while (seriesIterator.hasNext()) { 535 seriesIterator.next(); 536 count++; 537 } 538 return count; 539 } 540 541 /** 542 * This is called whenever a series is added or removed and the legend needs to be updated 543 */ 544 @Override protected void updateLegend() { 545 legend.getItems().clear(); 546 if (getData() != null) { 547 for (int seriesIndex = 0; seriesIndex < getData().size(); seriesIndex++) { 548 Series series = getData().get(seriesIndex); 549 Legend.LegendItem legenditem = new Legend.LegendItem(series.getName()); 550 String defaultColorStyleClass = seriesDefaultColorMap.get(series); 551 legenditem.getSymbol().getStyleClass().addAll("chart-bar", "series" + seriesIndex, "bar-legend-symbol", 552 defaultColorStyleClass); 553 legend.getItems().add(legenditem); 554 } 555 } 556 if (legend.getItems().size() > 0) { 557 if (getLegend() == null) { 558 setLegend(legend); 559 } 560 } else { 561 setLegend(null); 562 } 563 } 564 565 private Node createBar(Series series, int seriesIndex, final Data item, int itemIndex) { 566 Node bar = item.getNode(); 567 if (bar == null) { 568 bar = new StackPane(); 569 item.setNode(bar); 570 } 571 String defaultColorStyleClass = seriesDefaultColorMap.get(series); 572 bar.getStyleClass().setAll("chart-bar", "series" + seriesIndex, "data" + itemIndex, defaultColorStyleClass); 573 return bar; 574 } 575 576 private List<Data<X, Y>> getDataItem(Series<X, Y> series, int seriesIndex, int itemIndex, String category) { 577 Map<String, List<Data<X, Y>>> catmap = seriesCategoryMap.get(series); 578 return catmap != null ? catmap.get(category) != null ? catmap.get(category) : new ArrayList<Data<X, Y>>() : new ArrayList<Data<X, Y>>(); 579 } 580 581// -------------- STYLESHEET HANDLING ------------------------------------------------------------------------------ 582 583 /** 584 * Super-lazy instantiation pattern from Bill Pugh. 585 * @treatAsPrivate implementation detail 586 */ 587 private static class StyleableProperties { 588 589 private static final CssMetaData<StackedBarChart<?,?>,Number> CATEGORY_GAP = 590 new CssMetaData<StackedBarChart<?,?>,Number>("-fx-category-gap", 591 SizeConverter.getInstance(), 10.0) { 592 593 @Override 594 public boolean isSettable(StackedBarChart<?,?> node) { 595 return node.categoryGap == null || !node.categoryGap.isBound(); 596 } 597 598 @Override 599 public StyleableProperty<Number> getStyleableProperty(StackedBarChart<?,?> node) { 600 return (StyleableProperty<Number>)node.categoryGapProperty(); 601 } 602 }; 603 604 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 605 static { 606 607 final List<CssMetaData<? extends Styleable, ?>> styleables = 608 new ArrayList<CssMetaData<? extends Styleable, ?>>(XYChart.getClassCssMetaData()); 609 styleables.add(CATEGORY_GAP); 610 STYLEABLES = Collections.unmodifiableList(styleables); 611 } 612 } 613 614 /** 615 * @return The CssMetaData associated with this class, which may include the 616 * CssMetaData of its super classes. 617 */ 618 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 619 return StyleableProperties.STYLEABLES; 620 } 621 622 /** 623 * {@inheritDoc} 624 */ 625 @Override 626 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 627 return getClassCssMetaData(); 628 } 629 630 /** Pseudoclass indicating this is a vertical chart. */ 631 private static final PseudoClass VERTICAL_PSEUDOCLASS_STATE = 632 PseudoClass.getPseudoClass("vertical"); 633 /** Pseudoclass indicating this is a horizontal chart. */ 634 private static final PseudoClass HORIZONTAL_PSEUDOCLASS_STATE = 635 PseudoClass.getPseudoClass("horizontal"); 636 637}