callable) {
661 | super(chartId);
662 | this.callable = callable;
663 | }
664 |
665 | @Override
666 | protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
667 | int value = callable.call();
668 | if (value == 0) {
669 | // Null = skip the chart
670 | return null;
671 | }
672 | return new JsonObjectBuilder().appendField("value", value).build();
673 | }
674 | }
675 |
676 | /**
677 | * An extremely simple JSON builder.
678 | *
679 | * While this class is neither feature-rich nor the most performant one, it's sufficient enough
680 | * for its use-case.
681 | */
682 | public static class JsonObjectBuilder {
683 |
684 | private StringBuilder builder = new StringBuilder();
685 |
686 | private boolean hasAtLeastOneField = false;
687 |
688 | public JsonObjectBuilder() {
689 | builder.append("{");
690 | }
691 |
692 | /**
693 | * Appends a null field to the JSON.
694 | *
695 | * @param key The key of the field.
696 | * @return A reference to this object.
697 | */
698 | public JsonObjectBuilder appendNull(String key) {
699 | appendFieldUnescaped(key, "null");
700 | return this;
701 | }
702 |
703 | /**
704 | * Appends a string field to the JSON.
705 | *
706 | * @param key The key of the field.
707 | * @param value The value of the field.
708 | * @return A reference to this object.
709 | */
710 | public JsonObjectBuilder appendField(String key, String value) {
711 | if (value == null) {
712 | throw new IllegalArgumentException("JSON value must not be null");
713 | }
714 | appendFieldUnescaped(key, "\"" + escape(value) + "\"");
715 | return this;
716 | }
717 |
718 | /**
719 | * Appends an integer field to the JSON.
720 | *
721 | * @param key The key of the field.
722 | * @param value The value of the field.
723 | * @return A reference to this object.
724 | */
725 | public JsonObjectBuilder appendField(String key, int value) {
726 | appendFieldUnescaped(key, String.valueOf(value));
727 | return this;
728 | }
729 |
730 | /**
731 | * Appends an object to the JSON.
732 | *
733 | * @param key The key of the field.
734 | * @param object The object.
735 | * @return A reference to this object.
736 | */
737 | public JsonObjectBuilder appendField(String key, JsonObject object) {
738 | if (object == null) {
739 | throw new IllegalArgumentException("JSON object must not be null");
740 | }
741 | appendFieldUnescaped(key, object.toString());
742 | return this;
743 | }
744 |
745 | /**
746 | * Appends a string array to the JSON.
747 | *
748 | * @param key The key of the field.
749 | * @param values The string array.
750 | * @return A reference to this object.
751 | */
752 | public JsonObjectBuilder appendField(String key, String[] values) {
753 | if (values == null) {
754 | throw new IllegalArgumentException("JSON values must not be null");
755 | }
756 | String escapedValues =
757 | Arrays.stream(values)
758 | .map(value -> "\"" + escape(value) + "\"")
759 | .collect(Collectors.joining(","));
760 | appendFieldUnescaped(key, "[" + escapedValues + "]");
761 | return this;
762 | }
763 |
764 | /**
765 | * Appends an integer array to the JSON.
766 | *
767 | * @param key The key of the field.
768 | * @param values The integer array.
769 | * @return A reference to this object.
770 | */
771 | public JsonObjectBuilder appendField(String key, int[] values) {
772 | if (values == null) {
773 | throw new IllegalArgumentException("JSON values must not be null");
774 | }
775 | String escapedValues =
776 | Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
777 | appendFieldUnescaped(key, "[" + escapedValues + "]");
778 | return this;
779 | }
780 |
781 | /**
782 | * Appends an object array to the JSON.
783 | *
784 | * @param key The key of the field.
785 | * @param values The integer array.
786 | * @return A reference to this object.
787 | */
788 | public JsonObjectBuilder appendField(String key, JsonObject[] values) {
789 | if (values == null) {
790 | throw new IllegalArgumentException("JSON values must not be null");
791 | }
792 | String escapedValues =
793 | Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
794 | appendFieldUnescaped(key, "[" + escapedValues + "]");
795 | return this;
796 | }
797 |
798 | /**
799 | * Appends a field to the object.
800 | *
801 | * @param key The key of the field.
802 | * @param escapedValue The escaped value of the field.
803 | */
804 | private void appendFieldUnescaped(String key, String escapedValue) {
805 | if (builder == null) {
806 | throw new IllegalStateException("JSON has already been built");
807 | }
808 | if (key == null) {
809 | throw new IllegalArgumentException("JSON key must not be null");
810 | }
811 | if (hasAtLeastOneField) {
812 | builder.append(",");
813 | }
814 | builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
815 | hasAtLeastOneField = true;
816 | }
817 |
818 | /**
819 | * Builds the JSON string and invalidates this builder.
820 | *
821 | * @return The built JSON string.
822 | */
823 | public JsonObject build() {
824 | if (builder == null) {
825 | throw new IllegalStateException("JSON has already been built");
826 | }
827 | JsonObject object = new JsonObject(builder.append("}").toString());
828 | builder = null;
829 | return object;
830 | }
831 |
832 | /**
833 | * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
834 | *
835 | *
This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
836 | * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
837 | *
838 | * @param value The value to escape.
839 | * @return The escaped value.
840 | */
841 | private static String escape(String value) {
842 | final StringBuilder builder = new StringBuilder();
843 | for (int i = 0; i < value.length(); i++) {
844 | char c = value.charAt(i);
845 | if (c == '"') {
846 | builder.append("\\\"");
847 | } else if (c == '\\') {
848 | builder.append("\\\\");
849 | } else if (c <= '\u000F') {
850 | builder.append("\\u000").append(Integer.toHexString(c));
851 | } else if (c <= '\u001F') {
852 | builder.append("\\u00").append(Integer.toHexString(c));
853 | } else {
854 | builder.append(c);
855 | }
856 | }
857 | return builder.toString();
858 | }
859 |
860 | /**
861 | * A super simple representation of a JSON object.
862 | *
863 | *
This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
864 | * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
865 | * JsonObject)}.
866 | */
867 | public static class JsonObject {
868 |
869 | private final String value;
870 |
871 | private JsonObject(String value) {
872 | this.value = value;
873 | }
874 |
875 | @Override
876 | public String toString() {
877 | return value;
878 | }
879 | }
880 | }
881 | }
--------------------------------------------------------------------------------