[Merge][#638] Plan API 0.0.4, Tables for DataExtensions (#1010)

This commit is contained in:
Risto Lahtela 2019-04-14 10:31:30 +03:00 committed by GitHub
parent ded89b9d8c
commit 1dcae30d67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 2291 additions and 49 deletions

View File

@ -36,9 +36,16 @@ enum Capability {
/**
* ExtensionService, DataExtension API base package, PluginInfo, Conditional, Tab, TabInfo, TabOrder and BooleanProvider, DoubleProvider, PercentageProvider, NumberProvider, StringProvider annotations.
*/
DATA_EXTENSION_VALUES;
DATA_EXTENSION_VALUES,
/**
* DataExtension API table package, TableProvider, Table and Table.Factory
*/
DATA_EXTENSION_TABLES;
static Optional<Capability> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {

View File

@ -32,14 +32,14 @@ public enum ElementOrder {
* Represents text - value pair box.
*/
VALUES,
/**
* Represents tables.
*/
TABLE,
/**
* Represents graphs.
*/
GRAPH;
GRAPH,
/**
* Represents tables.
*/
TABLE;
public static String serialize(ElementOrder[] order) {
StringBuilder builder = new StringBuilder();

View File

@ -43,6 +43,9 @@ public enum FormatType {
NONE;
public static Optional<FormatType> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {

View File

@ -0,0 +1,49 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.annotation;
import com.djrapitops.plan.extension.icon.Color;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Method annotation to provide a Table.
* <p>
* Usage: {@code @TableProvider Table method(UUID playerUUID)}
* <p>
* Tables about players can have up to 4 columns.
* Tables about server can have up to 5 columns.
* <p>
* It is recommended to place each table on their own tab with a {@link Tab} annotation on the same method.
*
* @author Rsl1122
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TableProvider {
/**
* Determine the color of the table header.
*
* @return Preferred color. If none are specified defaults are used.
*/
Color tableColor() default Color.NONE;
}

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.extension.extractor;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.Group;
import com.djrapitops.plan.extension.annotation.*;
import com.djrapitops.plan.extension.table.Table;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@ -103,6 +104,8 @@ public final class ExtensionExtractor {
MethodAnnotations.get(method, Conditional.class).ifPresent(annotation -> methodAnnotations.put(method, Conditional.class, annotation));
MethodAnnotations.get(method, Tab.class).ifPresent(annotation -> methodAnnotations.put(method, Tab.class, annotation));
MethodAnnotations.get(method, TableProvider.class).ifPresent(annotation -> methodAnnotations.put(method, TableProvider.class, annotation));
}
if (methodAnnotations.isEmpty()) {
@ -166,6 +169,7 @@ public final class ExtensionExtractor {
validateDoubleProviderAnnotations();
validatePercentageProviderAnnotations();
validateStringProviderAnnotations();
validateTableProviderAnnotations();
}
private void validateBooleanProviderAnnotations() {
@ -238,6 +242,13 @@ public final class ExtensionExtractor {
}
}
private void validateTableProviderAnnotations() {
for (Method method : methodAnnotations.getMethodAnnotations(TableProvider.class).keySet()) {
validateReturnType(method, Table.class);
validateMethodArguments(method, false, UUID.class, String.class, Group.class);
}
}
private void validateConditionals() {
Collection<Conditional> conditionals = methodAnnotations.getAnnotations(Conditional.class);
Collection<BooleanProvider> conditionProviders = methodAnnotations.getAnnotations(BooleanProvider.class);

View File

@ -48,6 +48,9 @@ public enum Color {
NONE;
public static Optional<Color> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {

View File

@ -29,6 +29,9 @@ public enum Family {
BRAND;
public static Optional<Family> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {

View File

@ -0,0 +1,213 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.table;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Object for giving Plan table data.
* <p>
* Usage: {@code Table.builder().columnOne("columnName", new Icon(...)).addRow("Your", "Row", "Data").build()}
* <p>
* Tables about players can have up to 4 columns.
* Tables about server can have up to 5 columns.
* <p>
* Icon colors are ignored.
* <p>
* If a row has more values than the column limit, they are ignored.
* If a row has less values than table columns, a '-' is displayed to distinguish a missing value.
* <p>
* If a table has no columns or rows, it is ignored.
*
* @author Rsl1122
* @see com.djrapitops.plan.extension.annotation.TableProvider for associated annotation.
*/
public final class Table {
private final String[] columns;
private final Icon[] icons;
private final List<Object[]> rows;
private Table() {
/* Tables are constructed with the factory. */
columns = new String[5];
icons = new Icon[5];
rows = new ArrayList<>();
}
/**
* Create a new Table Factory.
*
* @return a new Table Factory.
*/
public static Table.Factory builder() {
return new Table.Factory();
}
public String[] getColumns() {
return columns;
}
public int getMaxColumnSize() {
int columnCount = 0;
for (String column : columns) {
if (column == null) {
break; // Prevent having one null column between two columns
}
columnCount++;
}
return columnCount;
}
public Icon[] getIcons() {
return icons;
}
public List<Object[]> getRows() {
return rows;
}
/**
* Factory for creating new {@link Table} objects.
*/
public static final class Factory {
private final Table building;
// These variable are related to implementation, and is used when the table is being fetched from the database.
Color color; // Table color is defined with TableProvider annotation.
String tableName; // tableName is defined by method name annotated by TableProvider.
String tabName; // Tab name is defined with Tab annotation
int tabPriority; // Tab priority is defined with TabOrder annotation
ElementOrder[] tabOrder; // Tab element order is defined with TabInfo annotation
Icon tabIcon; // Tab icon is defined with TabInfo annotation
private Factory() {
building = new Table();
}
private Factory column(int indx, String columnName, Icon icon) {
building.columns[indx] = columnName;
building.icons[indx] = icon != null ? Icon.called(icon.getName()).of(icon.getFamily()).build() : Icon.called("question").build();
return this;
}
/**
* Set first column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnOne(String columnName, Icon icon) {
return column(0, columnName, icon);
}
/**
* Set second column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnTwo(String columnName, Icon icon) {
return column(1, columnName, icon);
}
/**
* Set third column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnThree(String columnName, Icon icon) {
return column(2, columnName, icon);
}
/**
* Set fourth column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnFour(String columnName, Icon icon) {
return column(3, columnName, icon);
}
/**
* Set fifth column name and icon.
*
* @param columnName Name of the column.
* @param icon Icon of the column, color is ignored.
* @return Factory.
*/
public Factory columnFive(String columnName, Icon icon) {
return column(4, columnName, icon);
}
/**
* Add a row of values to the table.
*
* @param values One value per column you have defined, {@code Object#toString()} will be called on the objects.
* @return Factory.
* @throws IllegalArgumentException If given varargs for 'values' is null.
*/
public Factory addRow(Object... values) {
if (values == null) {
throw new IllegalArgumentException("'values' for Table#addRow can not be null!");
}
if (values.length == 0 || areAllValuesNull(values)) {
return this; // Ignore row when all values are null or no values are present.
}
building.rows.add(Arrays.copyOf(values, 5));
return this;
}
private boolean areAllValuesNull(Object[] values) {
boolean allNull = true;
for (Object value : values) {
if (value != null) {
allNull = false;
break;
}
}
return allNull;
}
/**
* Finish building the table.
*
* @return Finished Table object.
*/
public Table build() {
return building;
}
}
}

View File

@ -166,6 +166,20 @@ class ExtensionExtractorTest {
assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: java.lang.String", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
@Test
void tableProviderMustReturnTable() {
@PluginInfo(name = "Extension")
class Extension implements DataExtension {
@TableProvider
public Double method(UUID playerUUID) {
return null;
}
}
ExtensionExtractor underTest = new ExtensionExtractor(new Extension());
assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: com.djrapitops.plan.extension.table.Table", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage());
}
@Test
void booleanProviderCanNotSupplyItsOwnConditional() {
@PluginInfo(name = "Extension")

View File

@ -38,11 +38,10 @@ public class CapabilityServiceImplementation implements CapabilityService {
}
private static CapabilityServiceImplementation get() {
CapabilityService instance = CapabilityService.getInstance();
if (instance == null) {
if (CapabilityServiceHolder.service == null) {
return new CapabilityServiceImplementation();
}
return (CapabilityServiceImplementation) instance;
return (CapabilityServiceImplementation) CapabilityServiceHolder.service;
}
public static void notifyAboutEnable(boolean isEnabled) {

View File

@ -47,6 +47,9 @@ public class RemoveEverythingTransaction extends Transaction {
clearTable(ExtensionPlayerValueTable.TABLE_NAME);
clearTable(ExtensionServerValueTable.TABLE_NAME);
clearTable(ExtensionProviderTable.TABLE_NAME);
clearTable(ExtensionPlayerTableValueTable.TABLE_NAME);
clearTable(ExtensionServerTableValueTable.TABLE_NAME);
clearTable(ExtensionTableProviderTable.TABLE_NAME);
clearTable(ExtensionTabTable.TABLE_NAME);
clearTable(ExtensionPluginTable.TABLE_NAME);
clearTable(ExtensionIconTable.TABLE_NAME);

View File

@ -53,5 +53,8 @@ public class CreateTablesTransaction extends OperationCriticalTransaction {
execute(ExtensionProviderTable.createTableSQL(dbType));
execute(ExtensionPlayerValueTable.createTableSQL(dbType));
execute(ExtensionServerValueTable.createTableSQL(dbType));
execute(ExtensionTableProviderTable.createTableSQL(dbType));
execute(ExtensionPlayerTableValueTable.createTableSQL(dbType));
execute(ExtensionServerTableValueTable.createTableSQL(dbType));
}
}

View File

@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
@ -54,9 +55,15 @@ public class ExtensionIconTable {
}
public static void set3IconValuesToStatement(PreparedStatement statement, int parameterIndex, Icon icon) throws SQLException {
if (icon != null) {
statement.setString(parameterIndex, StringUtils.truncate(icon.getName(), 50));
statement.setString(parameterIndex + 1, icon.getFamily().name());
statement.setString(parameterIndex + 2, icon.getColor().name());
} else {
statement.setNull(parameterIndex, Types.VARCHAR);
statement.setNull(parameterIndex + 1, Types.VARCHAR);
statement.setNull(parameterIndex + 2, Types.VARCHAR);
}
}
private ExtensionIconTable() {

View File

@ -0,0 +1,60 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.sql.tables;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
import com.djrapitops.plan.db.sql.parsing.Sql;
import static com.djrapitops.plan.db.sql.parsing.Sql.INT;
/**
* Table information about 'plan_extension_user_table_values'.
*
* @author Rsl1122
*/
public class ExtensionPlayerTableValueTable {
public static final String TABLE_NAME = "plan_extension_user_table_values";
public static final String ID = "id";
public static final String TABLE_ID = "table_id";
public static final String USER_UUID = "uuid";
// All values can be null
public static final String VALUE_1 = "col_1_value";
public static final String VALUE_2 = "col_2_value";
public static final String VALUE_3 = "col_3_value";
public static final String VALUE_4 = "col_4_value";
private ExtensionPlayerTableValueTable() {
/* Static information class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableParser.create(TABLE_NAME, dbType)
.column(ID, INT).primaryKey()
.column(USER_UUID, Sql.varchar(36)).notNull()
.column(VALUE_1, Sql.varchar(50))
.column(VALUE_2, Sql.varchar(50))
.column(VALUE_3, Sql.varchar(50))
.column(VALUE_4, Sql.varchar(50))
.column(TABLE_ID, INT).notNull()
.foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID)
.build();
}
}

View File

@ -37,7 +37,7 @@ public class ExtensionProviderTable {
public static final String ID = "id";
public static final String PROVIDER_NAME = "name";
public static final String TEXT = "text"; // Can be null
public static final String TEXT = "text";
public static final String DESCRIPTION = "description"; // Can be null
public static final String PRIORITY = "priority";
public static final String GROUPABLE = "groupable"; // default false

View File

@ -0,0 +1,62 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.sql.tables;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
import com.djrapitops.plan.db.sql.parsing.Sql;
import static com.djrapitops.plan.db.sql.parsing.Sql.INT;
/**
* Table information about 'plan_extension_server_table_values'.
*
* @author Rsl1122
*/
public class ExtensionServerTableValueTable {
public static final String TABLE_NAME = "plan_extension_server_table_values";
public static final String ID = "id";
public static final String TABLE_ID = "table_id";
public static final String SERVER_UUID = "uuid";
// All values can be null
public static final String VALUE_1 = "col_1_value";
public static final String VALUE_2 = "col_2_value";
public static final String VALUE_3 = "col_3_value";
public static final String VALUE_4 = "col_4_value";
public static final String VALUE_5 = "col_5_value";
private ExtensionServerTableValueTable() {
/* Static information class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableParser.create(TABLE_NAME, dbType)
.column(ID, INT).primaryKey()
.column(SERVER_UUID, Sql.varchar(36)).notNull()
.column(VALUE_1, Sql.varchar(50))
.column(VALUE_2, Sql.varchar(50))
.column(VALUE_3, Sql.varchar(50))
.column(VALUE_4, Sql.varchar(50))
.column(VALUE_5, Sql.varchar(50))
.column(TABLE_ID, INT).notNull()
.foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID)
.build();
}
}

View File

@ -0,0 +1,100 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.db.sql.tables;
import com.djrapitops.plan.db.DBType;
import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
import com.djrapitops.plan.db.sql.parsing.Sql;
import com.djrapitops.plan.extension.icon.Color;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Table information about 'plan_extension_tables'.
*
* @author Rsl1122
*/
public class ExtensionTableProviderTable {
public static final String TABLE_NAME = "plan_extension_tables";
public static final String ID = "id";
public static final String PROVIDER_NAME = "name";
public static final String COLOR = "color";
public static final String CONDITION = "condition_name"; // Can be null, related to @Conditional
public static final String PLUGIN_ID = "plugin_id";
public static final String TAB_ID = "tab_id"; // Can be null, related to @Tab
// All columns can be null
public static final String COL_1 = "col_1_name";
public static final String COL_2 = "col_2_name";
public static final String COL_3 = "col_3_name";
public static final String COL_4 = "col_4_name";
public static final String COL_5 = "col_5_name";
// All icons can be null
public static final String ICON_1_ID = "icon_1_id";
public static final String ICON_2_ID = "icon_2_id";
public static final String ICON_3_ID = "icon_3_id";
public static final String ICON_4_ID = "icon_4_id";
public static final String ICON_5_ID = "icon_5_id";
public static final String STATEMENT_SELECT_TABLE_ID = "(" + SELECT + ID + FROM + TABLE_NAME +
WHERE + PROVIDER_NAME + "=?" +
AND + PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ")";
private ExtensionTableProviderTable() {
/* Static information class */
}
public static void set3PluginValuesToStatement(PreparedStatement statement, int parameterIndex, String providerName, String pluginName, UUID serverUUID) throws SQLException {
statement.setString(parameterIndex, providerName);
ExtensionPluginTable.set2PluginValuesToStatement(statement, parameterIndex + 1, pluginName, serverUUID);
}
public static String createTableSQL(DBType dbType) {
return CreateTableParser.create(TABLE_NAME, dbType)
.column(ID, INT).primaryKey()
.column(PROVIDER_NAME, Sql.varchar(50)).notNull()
.column(COLOR, Sql.varchar(25)).notNull().defaultValue("'" + Color.NONE.name() + "'")
.column(CONDITION, Sql.varchar(54)) // 50 + 4 for "not_"
.column(COL_1, Sql.varchar(50))
.column(COL_2, Sql.varchar(50))
.column(COL_3, Sql.varchar(50))
.column(COL_4, Sql.varchar(50))
.column(COL_5, Sql.varchar(50))
.column(PLUGIN_ID, INT).notNull()
.column(ICON_1_ID, INT)
.column(ICON_2_ID, INT)
.column(ICON_3_ID, INT)
.column(ICON_4_ID, INT)
.column(ICON_5_ID, INT)
.column(TAB_ID, INT)
.foreignKey(PLUGIN_ID, ExtensionPluginTable.TABLE_NAME, ExtensionPluginTable.ID)
.foreignKey(ICON_1_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(ICON_2_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(ICON_3_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(ICON_4_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(ICON_5_ID, ExtensionIconTable.TABLE_NAME, ExtensionIconTable.ID)
.foreignKey(TAB_ID, ExtensionTabTable.TABLE_NAME, ExtensionTabTable.ID)
.build();
}
}

View File

@ -124,6 +124,7 @@ public class DataProviderExtractor {
extractDataProviders(pluginInfo, tabs, conditions, PercentageProvider.class, PercentageDataProvider::placeToDataProviders);
extractDataProviders(pluginInfo, tabs, conditions, NumberProvider.class, NumberDataProvider::placeToDataProviders);
extractDataProviders(pluginInfo, tabs, conditions, StringProvider.class, StringDataProvider::placeToDataProviders);
extractDataProviders(pluginInfo, tabs, conditions, TableProvider.class, TableDataProvider::placeToDataProviders);
}
private <T extends Annotation> void extractDataProviders(PluginInfo pluginInfo, Map<Method, Tab> tabs, Map<Method, Conditional> conditions, Class<T> ofKind, DataProviderFactory<T> factory) {

View File

@ -0,0 +1,69 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.providers;
import com.djrapitops.plan.extension.annotation.Conditional;
import com.djrapitops.plan.extension.annotation.TableProvider;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.implementation.ProviderInformation;
import com.djrapitops.plan.extension.table.Table;
import java.lang.reflect.Method;
/**
* Represents a DataExtension API method annotated with {@link com.djrapitops.plan.extension.annotation.TableProvider} annotation.
* <p>
* Used to obtain data to place in the database.
* <p>
* Please note that not all {@link ProviderInformation} is present.
*
* @author Rsl1122
*/
public class TableDataProvider extends DataProvider<Table> {
private final Color tableColor;
private TableDataProvider(ProviderInformation providerInformation, MethodWrapper<Table> methodWrapper, Color tableColor) {
super(providerInformation, methodWrapper);
this.tableColor = tableColor;
}
public static void placeToDataProviders(
DataProviders dataProviders, Method method, TableProvider annotation,
Conditional condition, String tab, String pluginName
) {
MethodWrapper<Table> methodWrapper = new MethodWrapper<>(method, Table.class);
ProviderInformation providerInformation = new ProviderInformation(
pluginName, method.getName(), null, null, null, 0, tab, condition
);
dataProviders.put(new TableDataProvider(providerInformation, methodWrapper, annotation.tableColor()));
}
public static Color getTableColor(DataProvider<Table> provider) {
if (provider instanceof TableDataProvider) {
return ((TableDataProvider) provider).getTableColor();
}
return Color.NONE;
}
public Color getTableColor() {
return tableColor;
}
}

View File

@ -22,6 +22,7 @@ import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.DataProviderExtractor;
import com.djrapitops.plan.extension.implementation.TabInformation;
import com.djrapitops.plan.extension.implementation.providers.DataProviders;
import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIconTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.StorePluginTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.StoreTabInformationTransaction;
@ -47,6 +48,7 @@ public class ProviderValueGatherer {
private NumberProviderValueGatherer numberGatherer;
private DoubleAndPercentageProviderValueGatherer doubleAndPercentageGatherer;
private StringProviderValueGatherer stringGatherer;
private TableProviderValueGatherer tableGatherer;
public ProviderValueGatherer(
DataExtension extension,
@ -60,25 +62,24 @@ public class ProviderValueGatherer {
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
String pluginName = extractor.getPluginName();
UUID serverUUID = serverInfo.getServerUUID();
Database database = dbSystem.getDatabase();
DataProviders dataProviders = extractor.getDataProviders();
booleanGatherer = new BooleanProviderValueGatherer(
extractor.getPluginName(), extension,
serverInfo.getServerUUID(), dbSystem.getDatabase(),
extractor.getDataProviders(), logger
pluginName, extension, serverUUID, database, dataProviders, logger
);
numberGatherer = new NumberProviderValueGatherer(
extractor.getPluginName(), extension,
serverInfo.getServerUUID(), dbSystem.getDatabase(),
extractor.getDataProviders(), logger
pluginName, extension, serverUUID, database, dataProviders, logger
);
doubleAndPercentageGatherer = new DoubleAndPercentageProviderValueGatherer(
extractor.getPluginName(), extension,
serverInfo.getServerUUID(), dbSystem.getDatabase(),
extractor.getDataProviders(), logger
pluginName, extension, serverUUID, database, dataProviders, logger
);
stringGatherer = new StringProviderValueGatherer(
extractor.getPluginName(), extension,
serverInfo.getServerUUID(), dbSystem.getDatabase(),
extractor.getDataProviders(), logger
pluginName, extension, serverUUID, database, dataProviders, logger
);
tableGatherer = new TableProviderValueGatherer(
pluginName, extension, serverUUID, database, dataProviders, logger
);
}
@ -121,6 +122,7 @@ public class ProviderValueGatherer {
numberGatherer.gatherNumberDataOfPlayer(playerUUID, playerName, conditions);
doubleAndPercentageGatherer.gatherDoubleDataOfPlayer(playerUUID, playerName, conditions);
stringGatherer.gatherStringDataOfPlayer(playerUUID, playerName, conditions);
tableGatherer.gatherTableDataOfPlayer(playerUUID, playerName, conditions);
}
public void updateValues() {
@ -128,5 +130,6 @@ public class ProviderValueGatherer {
numberGatherer.gatherNumberDataOfServer(conditions);
doubleAndPercentageGatherer.gatherDoubleDataOfServer(conditions);
stringGatherer.gatherStringDataOfServer(conditions);
tableGatherer.gatherTableDataOfServer(conditions);
}
}

View File

@ -0,0 +1,131 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
import com.djrapitops.plan.db.Database;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.ProviderInformation;
import com.djrapitops.plan.extension.implementation.providers.DataProvider;
import com.djrapitops.plan.extension.implementation.providers.DataProviders;
import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
import com.djrapitops.plan.extension.implementation.providers.TableDataProvider;
import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIconTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreTableProviderTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerTableResultTransaction;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerTableResultTransaction;
import com.djrapitops.plan.extension.table.Table;
import com.djrapitops.plugin.logging.console.PluginLogger;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Gathers TableProvider method data.
*
* @author Rsl1122
*/
class TableProviderValueGatherer {
private final String pluginName;
private final DataExtension extension;
private final UUID serverUUID;
private final Database database;
private final DataProviders dataProviders;
private final PluginLogger logger;
TableProviderValueGatherer(
String pluginName, DataExtension extension,
UUID serverUUID, Database database,
DataProviders dataProviders,
PluginLogger logger
) {
this.pluginName = pluginName;
this.extension = extension;
this.serverUUID = serverUUID;
this.database = database;
this.dataProviders = dataProviders;
this.logger = logger;
}
void gatherTableDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) {
// Method parameters abstracted away so that same method can be used for all parameter types
// Same with Method result store transaction creation
Function<MethodWrapper<Table>, Callable<Table>> methodCaller = method -> () -> method.callMethod(extension, playerUUID, playerName);
BiFunction<MethodWrapper<Table>, Table, Transaction> storeTransactionCreator = (method, result) -> new StorePlayerTableResultTransaction(pluginName, serverUUID, method.getMethodName(), playerUUID, result);
for (DataProvider<Table> tableProvider : dataProviders.getPlayerMethodsByType(Table.class)) {
gatherTableDataOfProvider(methodCaller, storeTransactionCreator, conditions, tableProvider);
}
}
void gatherTableDataOfServer(Conditions conditions) {
// Method parameters abstracted away so that same method can be used for all parameter types
// Same with Method result store transaction creation
Function<MethodWrapper<Table>, Callable<Table>> methodCaller = method -> () -> method.callMethod(extension);
BiFunction<MethodWrapper<Table>, Table, Transaction> storeTransactionCreator = (method, result) -> new StoreServerTableResultTransaction(pluginName, serverUUID, method.getMethodName(), result);
for (DataProvider<Table> tableProvider : dataProviders.getServerMethodsByType(Table.class)) {
gatherTableDataOfProvider(methodCaller, storeTransactionCreator, conditions, tableProvider);
}
}
private void gatherTableDataOfProvider(
Function<MethodWrapper<Table>, Callable<Table>> methodCaller,
BiFunction<MethodWrapper<Table>, Table, Transaction> storeTransactionCreator,
Conditions conditions,
DataProvider<Table> tableProvider
) {
ProviderInformation providerInformation = tableProvider.getProviderInformation();
Optional<String> condition = providerInformation.getCondition();
if (condition.isPresent() && conditions.isNotFulfilled(condition.get())) {
return;
}
MethodWrapper<Table> method = tableProvider.getMethod();
Table result = getMethodResult(
methodCaller.apply(method),
throwable -> pluginName + " has invalid implementation, method " + method.getMethodName() + " threw exception: " + throwable.toString()
);
if (result == null) {
return;
}
for (Icon icon : result.getIcons()) {
if (icon != null) {
database.executeTransaction(new StoreIconTransaction(icon));
}
}
database.executeTransaction(new StoreTableProviderTransaction(serverUUID, providerInformation, TableDataProvider.getTableColor(tableProvider), result));
database.executeTransaction(storeTransactionCreator.apply(method, result));
}
private <T> T getMethodResult(Callable<T> callable, Function<Throwable, String> errorMsg) {
try {
return callable.call();
} catch (Exception | NoSuchFieldError | NoSuchMethodError e) {
logger.warn(errorMsg.apply(e));
return null;
}
}
}

View File

@ -36,6 +36,8 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
private final Map<String, ExtensionNumberData> numberData;
private final Map<String, ExtensionStringData> stringData;
private final List<ExtensionTableData> tableData;
private List<String> order;
// Table and Graph data will be added later.
@ -48,6 +50,8 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
percentageData = new HashMap<>();
numberData = new HashMap<>();
stringData = new HashMap<>();
tableData = new ArrayList<>();
}
public TabInformation getTabInformation() {
@ -78,6 +82,10 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
return Optional.ofNullable(stringData.get(providerName));
}
public List<ExtensionTableData> getTableData() {
return tableData;
}
@Override
public int compareTo(ExtensionTabData other) {
return Integer.compare(this.tabInformation.getTabPriority(), other.tabInformation.getTabPriority()); // Lower is first
@ -103,6 +111,8 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
this.percentageData.putAll(other.percentageData);
this.numberData.putAll(other.numberData);
this.stringData.putAll(other.stringData);
this.tableData.addAll(other.tableData);
}
public static class Factory {
@ -138,6 +148,11 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
return this;
}
public Factory putTableData(ExtensionTableData extensionTableData) {
data.tableData.add(extensionTableData);
return this;
}
private void createOrderingList() {
List<ExtensionDescriptive> descriptives = new ArrayList<>();
data.booleanData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
@ -154,6 +169,7 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
public ExtensionTabData build() {
createOrderingList();
Collections.sort(data.tableData);
return data;
}
}

View File

@ -0,0 +1,106 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.results;
import com.djrapitops.plan.data.element.TableContainer;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.table.Table;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Represents table data from a single TableProvider.
*
* @author Rsl1122
*/
public class ExtensionTableData implements Comparable<ExtensionTableData> {
private final String providerName;
private final Table table;
private final Color tableColor;
public ExtensionTableData(String providerName, Table table, Color tableColor) {
this.providerName = providerName;
this.table = table;
this.tableColor = tableColor;
}
public TableContainer getHtmlTable() {
String[] columns = table.getColumns();
Icon[] icons = table.getIcons();
List<Object[]> rows = table.getRows();
String[] header = buildHeader(columns, icons);
TableContainer htmlTable = new TableContainer(header);
if (rows.size() > 50) {
htmlTable.useJqueryDataTables(); // Use a jQuery data table since there are a lot of rows.
} else {
String colorName = com.djrapitops.plan.utilities.html.icon.Color.getByName(tableColor.name()).orElse(com.djrapitops.plan.utilities.html.icon.Color.NONE).getHtmlClass()
.replace("col-", ""); // TODO after PluginData deprecation, change this thing
htmlTable.setColor(colorName);
}
for (Object[] row : rows) {
htmlTable.addRow(Arrays.stream(row).map(value -> value != null ? value.toString() : null).toArray(Serializable[]::new));
}
return htmlTable;
}
private String[] buildHeader(String[] columns, Icon[] icons) {
ArrayList<String> header = new ArrayList<>();
for (int i = 0; i < columns.length; i++) {
String column = columns[i];
if (column == null) {
break;
}
header.add(com.djrapitops.plan.utilities.html.icon.Icon.fromExtensionIcon(icons[i]).toHtml() + ' ' + column);
}
return header.toArray(new String[0]);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ExtensionTableData)) return false;
ExtensionTableData that = (ExtensionTableData) o;
return providerName.equals(that.providerName) &&
tableColor == that.tableColor;
}
@Override
public int hashCode() {
return Objects.hash(providerName, tableColor);
}
@Override
public int compareTo(ExtensionTableData other) {
return String.CASE_INSENSITIVE_ORDER.compare(providerName, other.providerName);
}
public boolean isWideTable() {
return table.getMaxColumnSize() > 3;
}
}

View File

@ -19,10 +19,7 @@ package com.djrapitops.plan.extension.implementation.results.player;
import com.djrapitops.plan.extension.implementation.results.ExtensionInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.*;
/**
* Represents data of a single extension about a player.
@ -104,6 +101,31 @@ public class ExtensionPlayerData implements Comparable<ExtensionPlayerData> {
Collections.sort(data.tabs);
return data;
}
public Factory combine(Factory with) {
if (with != null) {
for (ExtensionTabData tab : with.build().getTabs()) {
Optional<ExtensionTabData> found = getTab(tab.getTabInformation().getTabName());
if (found.isPresent()) {
found.get().combine(tab);
} else {
addTab(tab);
}
}
}
return this;
}
public Optional<ExtensionTabData> getTab(String tabName) {
for (ExtensionTabData tab : data.tabs) {
if (tabName.equals(tab.getTabInformation().getTabName())) {
return Optional.of(tab);
}
}
return Optional.empty();
}
}
}

View File

@ -68,9 +68,29 @@ public class ExtensionPlayerDataQuery implements Query<Map<UUID, List<ExtensionP
Map<UUID, List<ExtensionInformation>> extensionsByServerUUID = db.query(ExtensionInformationQueries.allExtensions());
Map<Integer, ExtensionPlayerData.Factory> extensionDataByPluginID = db.query(fetchIncompletePlayerDataByPluginID());
Map<Integer, ExtensionPlayerData.Factory> tableDataByPluginID = db.query(new ExtensionPlayerTablesQuery(playerUUID));
combine(extensionDataByPluginID, tableDataByPluginID);
return flatMapByServerUUID(extensionsByServerUUID, extensionDataByPluginID);
}
private void combine(
Map<Integer, ExtensionPlayerData.Factory> extensionDataByPluginID,
Map<Integer, ExtensionPlayerData.Factory> aggregates
) {
for (Map.Entry<Integer, ExtensionPlayerData.Factory> entry : aggregates.entrySet()) {
Integer pluginID = entry.getKey();
ExtensionPlayerData.Factory data = entry.getValue();
ExtensionPlayerData.Factory found = extensionDataByPluginID.get(pluginID);
if (found == null) {
extensionDataByPluginID.put(pluginID, data);
} else {
found.combine(data);
}
}
}
private Map<UUID, List<ExtensionPlayerData>> flatMapByServerUUID(Map<UUID, List<ExtensionInformation>> extensionsByServerUUID, Map<Integer, ExtensionPlayerData.Factory> extensionDataByPluginID) {
Map<UUID, List<ExtensionPlayerData>> extensionDataByServerUUID = new HashMap<>();

View File

@ -0,0 +1,287 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.queries;
import com.djrapitops.plan.db.SQLDB;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.sql.tables.ExtensionIconTable;
import com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTabTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Family;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.TabInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import com.djrapitops.plan.extension.implementation.results.ExtensionTableData;
import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData;
import com.djrapitops.plan.extension.table.Table;
import com.djrapitops.plan.extension.table.TableAccessor;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Query player tables from tableprovider table.
* <p>
* Returns Map: PluginID - ExtensionPlayerData.Factory.
* <p>
* How it is done:
* - TableProviders are queried and formed into Table.Factory objects sorted into a multi-map: PluginID - TableID - Table.Factory
* - Table values are queried and merged into the above multimap
* - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data
* - (Tab Name can be empty.)
* - Multi-map is sorted into ExtensionPlayerData objects by PluginID, one per ID
* <p>
* There are multiple data extraction methods to make extracting the value query easier.
*
* @author Rsl1122
*/
public class ExtensionPlayerTablesQuery implements Query<Map<Integer, ExtensionPlayerData.Factory>> {
private final UUID playerUUID;
public ExtensionPlayerTablesQuery(UUID playerUUID) {
this.playerUUID = playerUUID;
}
private static Map<Integer, ExtensionPlayerData.Factory> flatMapByPluginID(Map<Integer, Map<String, ExtensionTabData.Factory>> tabDataByPluginID) {
Map<Integer, ExtensionPlayerData.Factory> dataByPluginID = new HashMap<>();
for (Map.Entry<Integer, Map<String, ExtensionTabData.Factory>> entry : tabDataByPluginID.entrySet()) {
Integer pluginID = entry.getKey();
ExtensionPlayerData.Factory data = dataByPluginID.getOrDefault(pluginID, new ExtensionPlayerData.Factory(pluginID));
for (ExtensionTabData.Factory tabData : entry.getValue().values()) {
data.addTab(tabData.build());
}
dataByPluginID.put(pluginID, data);
}
return dataByPluginID;
}
@Override
public Map<Integer, ExtensionPlayerData.Factory> executeQuery(SQLDB db) {
Map<Integer, Map<Integer, Table.Factory>> tablesByPluginIDAndTableID = db.query(queryTableProviders());
Map<Integer, Map<Integer, Table.Factory>> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID));
Map<Integer, Map<String, ExtensionTabData.Factory>> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues);
return flatMapByPluginID(tabDataByPluginID);
}
/**
* @param tablesByPluginIDAndTableID {@code <Plugin ID - <Table ID - Table.Factory>>}
* @return {@code <Plugin ID - <Tab name - ExtensionTabData.Factory>}
*/
private Map<Integer, Map<String, ExtensionTabData.Factory>> mapToTabsByPluginID(Map<Integer, Map<Integer, Table.Factory>> tablesByPluginIDAndTableID) {
Map<Integer, Map<String, ExtensionTabData.Factory>> byPluginID = new HashMap<>();
for (Map.Entry<Integer, Map<Integer, Table.Factory>> entry : tablesByPluginIDAndTableID.entrySet()) {
Integer pluginID = entry.getKey();
Map<String, ExtensionTabData.Factory> byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>());
for (Table.Factory table : entry.getValue().values()) {
// Extra Table information
String tableName = TableAccessor.getTableName(table);
Color tableColor = TableAccessor.getColor(table);
// Extra tab information
String tabName = TableAccessor.getTabName(table);
int tabPriority = TableAccessor.getTabPriority(table);
ElementOrder[] tabOrder = TableAccessor.getTabOrder(table);
Icon tabIcon = TableAccessor.getTabIcon(table);
ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority)));
tab.putTableData(new ExtensionTableData(
tableName, table.build(), tableColor
));
byTabName.put(tabName, tab);
}
byPluginID.put(pluginID, byTabName);
}
return byPluginID;
}
// Map: <Plugin ID - <Table ID - Table.Factory>>
private Query<Map<Integer, Map<Integer, Table.Factory>>> queryTableValues(Map<Integer, Map<Integer, Table.Factory>> tables) {
String selectTableValues = SELECT +
ExtensionTableProviderTable.PLUGIN_ID + ',' +
ExtensionPlayerTableValueTable.TABLE_ID + ',' +
ExtensionPlayerTableValueTable.VALUE_1 + ',' +
ExtensionPlayerTableValueTable.VALUE_2 + ',' +
ExtensionPlayerTableValueTable.VALUE_3 + ',' +
ExtensionPlayerTableValueTable.VALUE_4 +
FROM + ExtensionPlayerTableValueTable.TABLE_NAME +
INNER_JOIN + ExtensionTableProviderTable.TABLE_NAME + " on " + ExtensionTableProviderTable.TABLE_NAME + '.' + ExtensionTableProviderTable.ID + '=' + ExtensionPlayerTableValueTable.TABLE_NAME + '.' + ExtensionPlayerTableValueTable.TABLE_ID +
WHERE + ExtensionPlayerTableValueTable.USER_UUID + "=?";
return new QueryStatement<Map<Integer, Map<Integer, Table.Factory>>>(selectTableValues, 10000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Map<Integer, Map<Integer, Table.Factory>> processResults(ResultSet set) throws SQLException {
while (set.next()) {
Table.Factory table = getTable(set);
if (table == null) {
continue;
}
Object[] row = extractTableRow(set);
if (row.length > 0) {
table.addRow(row);
}
}
return tables;
}
private Table.Factory getTable(ResultSet set) throws SQLException {
int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID);
Map<Integer, Table.Factory> byTableID = tables.get(pluginID);
if (byTableID == null) {
return null;
}
int tableID = set.getInt(ExtensionPlayerTableValueTable.TABLE_ID);
return byTableID.get(tableID);
}
};
}
private Object[] extractTableRow(ResultSet set) throws SQLException {
List<Object> row = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
String columnName = "col_" + i + "_value"; // See ExtensionPlayerTableValueTable.VALUE_1
String value = set.getString(columnName);
if (value == null) {
return row.toArray(new Object[0]);
}
row.add(value);
}
return row.toArray(new Object[0]);
}
// Map: <Plugin ID - <Table ID - Table.Factory>>
private Query<Map<Integer, Map<Integer, Table.Factory>>> queryTableProviders() {
String selectTables = SELECT +
"p1." + ExtensionTableProviderTable.ID + " as table_id," +
"p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," +
"p1." + ExtensionTableProviderTable.PROVIDER_NAME + " as table_name," +
"p1." + ExtensionTableProviderTable.COLOR + " as table_color," +
ExtensionTableProviderTable.COL_1 + ',' +
ExtensionTableProviderTable.COL_2 + ',' +
ExtensionTableProviderTable.COL_3 + ',' +
ExtensionTableProviderTable.COL_4 + ',' +
"t1." + ExtensionTabTable.TAB_NAME + " as tab_name," +
"t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," +
"t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," +
"i1." + ExtensionIconTable.ICON_NAME + " as i1_name," +
"i1." + ExtensionIconTable.FAMILY + " as i1_family," +
"i1." + ExtensionIconTable.COLOR + " as i1_color," +
"i2." + ExtensionIconTable.ICON_NAME + " as i2_name," +
"i2." + ExtensionIconTable.FAMILY + " as i2_family," +
"i2." + ExtensionIconTable.COLOR + " as i2_color," +
"i3." + ExtensionIconTable.ICON_NAME + " as i3_name," +
"i3." + ExtensionIconTable.FAMILY + " as i3_family," +
"i3." + ExtensionIconTable.COLOR + " as i3_color," +
"i4." + ExtensionIconTable.ICON_NAME + " as i4_name," +
"i4." + ExtensionIconTable.FAMILY + " as i4_family," +
"i4." + ExtensionIconTable.COLOR + " as i4_color," +
"i6." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," +
"i6." + ExtensionIconTable.FAMILY + " as tab_icon_family," +
"i6." + ExtensionIconTable.COLOR + " as tab_icon_color" +
FROM + ExtensionTableProviderTable.TABLE_NAME + " p1" +
INNER_JOIN + ExtensionPlayerTableValueTable.TABLE_NAME + " v1 on v1." + ExtensionPlayerTableValueTable.TABLE_ID + "=p1." + ExtensionTableProviderTable.ID +
LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionTableProviderTable.TAB_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_1_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_2_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i3 on i3." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_3_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i4 on i4." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_4_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i6 on i6." + ExtensionIconTable.ID + "=t1." + ExtensionTabTable.ICON_ID +
WHERE + "v1." + ExtensionPlayerTableValueTable.USER_UUID + "=?";
return new QueryStatement<Map<Integer, Map<Integer, Table.Factory>>>(selectTables, 100) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, playerUUID.toString());
}
@Override
public Map<Integer, Map<Integer, Table.Factory>> processResults(ResultSet set) throws SQLException {
Map<Integer, Map<Integer, Table.Factory>> byPluginID = new HashMap<>();
while (set.next()) {
int pluginID = set.getInt("plugin_id");
Map<Integer, Table.Factory> byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>());
int tableID = set.getInt("table_id");
Table.Factory table = Table.builder();
extractColumns(set, table);
TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE));
TableAccessor.setTableName(table, set.getString("table_name"));
TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse(""));
TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100));
TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values()));
TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon"));
byTableID.put(tableID, table);
byPluginID.put(pluginID, byTableID);
}
return byPluginID;
}
};
}
private void extractColumns(ResultSet set, Table.Factory table) throws SQLException {
String col1 = set.getString(ExtensionTableProviderTable.COL_1);
if (col1 != null) {
table.columnOne(col1, extractIcon(set, "i1"));
}
String col2 = set.getString(ExtensionTableProviderTable.COL_2);
if (col2 != null) {
table.columnTwo(col2, extractIcon(set, "i2"));
}
String col3 = set.getString(ExtensionTableProviderTable.COL_3);
if (col3 != null) {
table.columnThree(col3, extractIcon(set, "i3"));
}
String col4 = set.getString(ExtensionTableProviderTable.COL_4);
if (col4 != null) {
table.columnFour(col4, extractIcon(set, "i4"));
}
}
private Icon extractIcon(ResultSet set, String iconColumnName) throws SQLException {
String iconName = set.getString(iconColumnName + "_name");
if (iconName == null) {
return null;
}
return new Icon(
Family.getByName(set.getString(iconColumnName + "_family")).orElse(Family.SOLID),
iconName,
Color.getByName(set.getString(iconColumnName + "_color")).orElse(Color.NONE)
);
}
}

View File

@ -83,6 +83,7 @@ public class ExtensionServerDataQuery implements Query<List<ExtensionServerData>
combine(extensionDataByPluginID, db.query(new ExtensionAggregateDoublesQuery(serverUUID)));
combine(extensionDataByPluginID, db.query(new ExtensionAggregateNumbersQuery(serverUUID)));
combine(extensionDataByPluginID, db.query(new ExtensionAggregatePercentagesQuery(serverUUID)));
combine(extensionDataByPluginID, db.query(new ExtensionServerTablesQuery(serverUUID)));
return combineWithExtensionInfo(extensionsOfServer, extensionDataByPluginID);
}

View File

@ -0,0 +1,284 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.queries;
import com.djrapitops.plan.db.SQLDB;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.sql.tables.ExtensionIconTable;
import com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTabTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Family;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.TabInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import com.djrapitops.plan.extension.implementation.results.ExtensionTableData;
import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData;
import com.djrapitops.plan.extension.table.Table;
import com.djrapitops.plan.extension.table.TableAccessor;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
/**
* Query server tables from tableprovider table.
* <p>
* Returns Map: PluginID - ExtensionServerData.Factory.
* <p>
* How it is done:
* - TableProviders are queried and formed into Table.Factory objects sorted into a multi-map: PluginID - TableID - Table.Factory
* - Table values are queried and merged into the above multimap
* - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data
* - (Tab Name can be empty.)
* - Multi-map is sorted into ExtensionServerData objects by PluginID, one per ID
* <p>
* There are multiple data extraction methods to make extracting the value query easier.
*
* @author Rsl1122
*/
public class ExtensionServerTablesQuery implements Query<Map<Integer, ExtensionServerData.Factory>> {
private final UUID serverUUID;
public ExtensionServerTablesQuery(UUID serverUUID) {
this.serverUUID = serverUUID;
}
@Override
public Map<Integer, ExtensionServerData.Factory> executeQuery(SQLDB db) {
Map<Integer, Map<Integer, Table.Factory>> tablesByPluginIDAndTableID = db.query(queryTableProviders());
Map<Integer, Map<Integer, Table.Factory>> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID));
Map<Integer, Map<String, ExtensionTabData.Factory>> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues);
return ExtensionServerDataQuery.flatMapToServerData(tabDataByPluginID);
}
/**
* @param tablesByPluginIDAndTableID {@code <Plugin ID - <Table ID - Table.Factory>>}
* @return {@code <Plugin ID - <Tab name - ExtensionTabData.Factory>}
*/
private Map<Integer, Map<String, ExtensionTabData.Factory>> mapToTabsByPluginID(Map<Integer, Map<Integer, Table.Factory>> tablesByPluginIDAndTableID) {
Map<Integer, Map<String, ExtensionTabData.Factory>> byPluginID = new HashMap<>();
for (Map.Entry<Integer, Map<Integer, Table.Factory>> entry : tablesByPluginIDAndTableID.entrySet()) {
Integer pluginID = entry.getKey();
Map<String, ExtensionTabData.Factory> byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>());
for (Table.Factory table : entry.getValue().values()) {
// Extra Table information
String tableName = TableAccessor.getTableName(table);
Color tableColor = TableAccessor.getColor(table);
// Extra tab information
String tabName = TableAccessor.getTabName(table);
int tabPriority = TableAccessor.getTabPriority(table);
ElementOrder[] tabOrder = TableAccessor.getTabOrder(table);
Icon tabIcon = TableAccessor.getTabIcon(table);
ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority)));
tab.putTableData(new ExtensionTableData(
tableName, table.build(), tableColor
));
byTabName.put(tabName, tab);
}
byPluginID.put(pluginID, byTabName);
}
return byPluginID;
}
// Map: <Plugin ID - <Table ID - Table.Factory>>
private Query<Map<Integer, Map<Integer, Table.Factory>>> queryTableValues(Map<Integer, Map<Integer, Table.Factory>> tables) {
String selectTableValues = SELECT +
ExtensionTableProviderTable.PLUGIN_ID + ',' +
ExtensionServerTableValueTable.TABLE_ID + ',' +
ExtensionServerTableValueTable.VALUE_1 + ',' +
ExtensionServerTableValueTable.VALUE_2 + ',' +
ExtensionServerTableValueTable.VALUE_3 + ',' +
ExtensionServerTableValueTable.VALUE_4 + ',' +
ExtensionServerTableValueTable.VALUE_5 +
FROM + ExtensionServerTableValueTable.TABLE_NAME +
INNER_JOIN + ExtensionTableProviderTable.TABLE_NAME + " on " + ExtensionTableProviderTable.TABLE_NAME + '.' + ExtensionTableProviderTable.ID + '=' + ExtensionServerTableValueTable.TABLE_NAME + '.' + ExtensionServerTableValueTable.TABLE_ID +
WHERE + ExtensionServerTableValueTable.SERVER_UUID + "=?";
return new QueryStatement<Map<Integer, Map<Integer, Table.Factory>>>(selectTableValues, 10000) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
}
@Override
public Map<Integer, Map<Integer, Table.Factory>> processResults(ResultSet set) throws SQLException {
while (set.next()) {
Table.Factory table = getTable(set);
if (table == null) {
continue;
}
Object[] row = extractTableRow(set);
if (row.length > 0) {
table.addRow(row);
}
}
return tables;
}
private Table.Factory getTable(ResultSet set) throws SQLException {
int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID);
Map<Integer, Table.Factory> byTableID = tables.get(pluginID);
if (byTableID == null) {
return null;
}
int tableID = set.getInt(ExtensionServerTableValueTable.TABLE_ID);
return byTableID.get(tableID);
}
};
}
private Object[] extractTableRow(ResultSet set) throws SQLException {
List<Object> row = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
String columnName = "col_" + i + "_value"; // See ExtensionServerTableValueTable.VALUE_1
String value = set.getString(columnName);
if (value == null) {
return row.toArray(new Object[0]);
}
row.add(value);
}
return row.toArray(new Object[0]);
}
// Map: <Plugin ID - <Table ID - Table.Factory>>
private Query<Map<Integer, Map<Integer, Table.Factory>>> queryTableProviders() {
String selectTables = SELECT +
"p1." + ExtensionTableProviderTable.ID + " as table_id," +
"p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," +
"p1." + ExtensionTableProviderTable.PROVIDER_NAME + " as table_name," +
"p1." + ExtensionTableProviderTable.COLOR + " as table_color," +
ExtensionTableProviderTable.COL_1 + ',' +
ExtensionTableProviderTable.COL_2 + ',' +
ExtensionTableProviderTable.COL_3 + ',' +
ExtensionTableProviderTable.COL_4 + ',' +
ExtensionTableProviderTable.COL_5 + ',' +
"t1." + ExtensionTabTable.TAB_NAME + " as tab_name," +
"t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," +
"t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," +
"i1." + ExtensionIconTable.ICON_NAME + " as i1_name," +
"i1." + ExtensionIconTable.FAMILY + " as i1_family," +
"i1." + ExtensionIconTable.COLOR + " as i1_color," +
"i2." + ExtensionIconTable.ICON_NAME + " as i2_name," +
"i2." + ExtensionIconTable.FAMILY + " as i2_family," +
"i2." + ExtensionIconTable.COLOR + " as i2_color," +
"i3." + ExtensionIconTable.ICON_NAME + " as i3_name," +
"i3." + ExtensionIconTable.FAMILY + " as i3_family," +
"i3." + ExtensionIconTable.COLOR + " as i3_color," +
"i4." + ExtensionIconTable.ICON_NAME + " as i4_name," +
"i4." + ExtensionIconTable.FAMILY + " as i4_family," +
"i4." + ExtensionIconTable.COLOR + " as i4_color," +
"i5." + ExtensionIconTable.ICON_NAME + " as i5_name," +
"i5." + ExtensionIconTable.FAMILY + " as i5_family," +
"i5." + ExtensionIconTable.COLOR + " as i5_color," +
"i6." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," +
"i6." + ExtensionIconTable.FAMILY + " as tab_icon_family," +
"i6." + ExtensionIconTable.COLOR + " as tab_icon_color" +
FROM + ExtensionTableProviderTable.TABLE_NAME + " p1" +
INNER_JOIN + ExtensionServerTableValueTable.TABLE_NAME + " v1 on v1." + ExtensionServerTableValueTable.TABLE_ID + "=p1." + ExtensionTableProviderTable.ID +
LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionTableProviderTable.TAB_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_1_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_2_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i3 on i3." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_3_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i4 on i4." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_4_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i5 on i5." + ExtensionIconTable.ID + "=p1." + ExtensionTableProviderTable.ICON_5_ID +
LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i6 on i6." + ExtensionIconTable.ID + "=t1." + ExtensionTabTable.ICON_ID +
WHERE + "v1." + ExtensionServerTableValueTable.SERVER_UUID + "=?";
return new QueryStatement<Map<Integer, Map<Integer, Table.Factory>>>(selectTables, 100) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
}
@Override
public Map<Integer, Map<Integer, Table.Factory>> processResults(ResultSet set) throws SQLException {
Map<Integer, Map<Integer, Table.Factory>> byPluginID = new HashMap<>();
while (set.next()) {
int pluginID = set.getInt("plugin_id");
Map<Integer, Table.Factory> byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>());
int tableID = set.getInt("table_id");
Table.Factory table = Table.builder();
extractColumns(set, table);
TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE));
TableAccessor.setTableName(table, set.getString("table_name"));
TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse(""));
TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100));
TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values()));
TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon"));
byTableID.put(tableID, table);
byPluginID.put(pluginID, byTableID);
}
return byPluginID;
}
};
}
private void extractColumns(ResultSet set, Table.Factory table) throws SQLException {
String col1 = set.getString(ExtensionTableProviderTable.COL_1);
if (col1 != null) {
table.columnOne(col1, extractIcon(set, "i1"));
}
String col2 = set.getString(ExtensionTableProviderTable.COL_2);
if (col2 != null) {
table.columnTwo(col2, extractIcon(set, "i2"));
}
String col3 = set.getString(ExtensionTableProviderTable.COL_3);
if (col3 != null) {
table.columnThree(col3, extractIcon(set, "i3"));
}
String col4 = set.getString(ExtensionTableProviderTable.COL_4);
if (col4 != null) {
table.columnFour(col4, extractIcon(set, "i4"));
}
String col5 = set.getString(ExtensionTableProviderTable.COL_5);
if (col5 != null) {
table.columnFive(col5, extractIcon(set, "i5"));
}
}
private Icon extractIcon(ResultSet set, String iconColumnName) throws SQLException {
String iconName = set.getString(iconColumnName + "_name");
if (iconName == null) {
return null;
}
return new Icon(
Family.getByName(set.getString(iconColumnName + "_family")).orElse(Family.SOLID),
iconName,
Color.getByName(set.getString(iconColumnName + "_color")).orElse(Color.NONE)
);
}
}

View File

@ -68,14 +68,14 @@ public class StoreTabInformationTransaction extends Transaction {
" SET " +
ExtensionTabTable.TAB_PRIORITY + "=?," +
ExtensionTabTable.ELEMENT_ORDER + "=?," +
ExtensionTabTable.ICON_ID + "=" + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + "," +
ExtensionTabTable.ICON_ID + "=" + ExtensionIconTable.STATEMENT_SELECT_ICON_ID +
WHERE + ExtensionTabTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
AND + ExtensionTabTable.TAB_NAME + "=?";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, tabInformation.getTabPriority());
statement.setString(2, tabInformation.getTabElementOrder().map(ElementOrder::serialize).orElse(null));
statement.setString(2, ElementOrder.serialize(tabInformation.getTabElementOrder().orElse(ElementOrder.values())));
ExtensionIconTable.set3IconValuesToStatement(statement, 3, tabInformation.getTabIcon());
ExtensionPluginTable.set2PluginValuesToStatement(statement, 6, pluginName, serverUUID);
statement.setString(8, tabInformation.getTabName());
@ -88,14 +88,14 @@ public class StoreTabInformationTransaction extends Transaction {
ExtensionTabTable.TAB_NAME + "," +
ExtensionTabTable.ELEMENT_ORDER + "," +
ExtensionTabTable.TAB_PRIORITY + "," +
ExtensionTabTable.ICON_ID +
ExtensionTabTable.ICON_ID + "," +
ExtensionTabTable.PLUGIN_ID +
") VALUES (?,?,?," + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + "," + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ")";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tabInformation.getTabName());
statement.setString(2, tabInformation.getTabElementOrder().map(ElementOrder::serialize).orElse(null));
statement.setString(2, ElementOrder.serialize(tabInformation.getTabElementOrder().orElse(ElementOrder.values())));
statement.setInt(3, tabInformation.getTabPriority());
ExtensionIconTable.set3IconValuesToStatement(statement, 4, tabInformation.getTabIcon());
ExtensionPluginTable.set2PluginValuesToStatement(statement, 7, pluginName, serverUUID);

View File

@ -0,0 +1,173 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.transactions.providers;
import com.djrapitops.plan.db.access.ExecStatement;
import com.djrapitops.plan.db.access.Executable;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.ExtensionIconTable;
import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTabTable;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.ProviderInformation;
import com.djrapitops.plan.extension.table.Table;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.AND;
import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE;
import static com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable.*;
/**
* Transaction to store information about a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}.
*
* @author Rsl1122
*/
public class StoreTableProviderTransaction extends Transaction {
private final UUID serverUUID;
private final ProviderInformation providerInformation;
private final Color tableColor;
private final Table table;
public StoreTableProviderTransaction(UUID serverUUID, ProviderInformation providerInformation, Color tableColor, Table table) {
this.providerInformation = providerInformation;
this.tableColor = tableColor;
this.table = table;
this.serverUUID = serverUUID;
}
@Override
protected void performOperations() {
execute(storeProvider());
}
private Executable storeProvider() {
return connection -> {
if (!updateProvider().execute(connection)) {
return insertProvider().execute(connection);
}
return false;
};
}
private Executable updateProvider() {
String[] columns = table.getColumns();
Icon[] icons = table.getIcons();
String sql = "UPDATE " + TABLE_NAME + " SET " +
COLOR + "=?," +
COL_1 + "=?," +
COL_2 + "=?," +
COL_3 + "=?," +
COL_4 + "=?," +
COL_5 + "=?," +
CONDITION + "=?," +
TAB_ID + '=' + ExtensionTabTable.STATEMENT_SELECT_TAB_ID + ',' +
ICON_1_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ICON_2_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ICON_3_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ICON_4_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ICON_5_ID + '=' + ExtensionIconTable.STATEMENT_SELECT_ICON_ID +
WHERE + PROVIDER_NAME + "=?" +
AND + PLUGIN_ID + '=' + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID;
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, tableColor.name());
setStringOrNull(statement, 2, columns[0]);
setStringOrNull(statement, 3, columns[1]);
setStringOrNull(statement, 4, columns[2]);
setStringOrNull(statement, 5, columns[3]);
setStringOrNull(statement, 6, columns[4]);
setStringOrNull(statement, 7, providerInformation.getCondition().orElse(null));
ExtensionTabTable.set3TabValuesToStatement(statement, 8, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID);
ExtensionIconTable.set3IconValuesToStatement(statement, 11, icons[0]);
ExtensionIconTable.set3IconValuesToStatement(statement, 14, icons[1]);
ExtensionIconTable.set3IconValuesToStatement(statement, 17, icons[2]);
ExtensionIconTable.set3IconValuesToStatement(statement, 20, icons[3]);
ExtensionIconTable.set3IconValuesToStatement(statement, 23, icons[4]);
statement.setString(26, providerInformation.getName());
ExtensionPluginTable.set2PluginValuesToStatement(statement, 27, providerInformation.getPluginName(), serverUUID);
}
};
}
private Executable insertProvider() {
String[] columns = table.getColumns();
Icon[] icons = table.getIcons();
String sql = "INSERT INTO " + TABLE_NAME + '(' +
PROVIDER_NAME + ',' +
COLOR + ',' +
COL_1 + ',' +
COL_2 + ',' +
COL_3 + ',' +
COL_4 + ',' +
COL_5 + ',' +
CONDITION + ',' +
TAB_ID + ',' +
PLUGIN_ID + ',' +
ICON_1_ID + ',' +
ICON_2_ID + ',' +
ICON_3_ID + ',' +
ICON_4_ID + ',' +
ICON_5_ID +
") VALUES (?,?,?,?,?,?,?,?," +
ExtensionTabTable.STATEMENT_SELECT_TAB_ID + ',' +
ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ',' +
ExtensionIconTable.STATEMENT_SELECT_ICON_ID + ')';
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, providerInformation.getName());
statement.setString(2, tableColor.name());
setStringOrNull(statement, 3, columns[0]);
setStringOrNull(statement, 4, columns[1]);
setStringOrNull(statement, 5, columns[2]);
setStringOrNull(statement, 6, columns[3]);
setStringOrNull(statement, 7, columns[4]);
setStringOrNull(statement, 8, providerInformation.getCondition().orElse(null));
ExtensionTabTable.set3TabValuesToStatement(statement, 9, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID);
ExtensionPluginTable.set2PluginValuesToStatement(statement, 12, providerInformation.getPluginName(), serverUUID);
ExtensionIconTable.set3IconValuesToStatement(statement, 14, icons[0]);
ExtensionIconTable.set3IconValuesToStatement(statement, 17, icons[1]);
ExtensionIconTable.set3IconValuesToStatement(statement, 20, icons[2]);
ExtensionIconTable.set3IconValuesToStatement(statement, 23, icons[3]);
ExtensionIconTable.set3IconValuesToStatement(statement, 26, icons[4]);
}
};
}
private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
if (value != null) {
statement.setString(index, value);
} else {
statement.setNull(index, Types.VARCHAR);
}
}
}

View File

@ -0,0 +1,155 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.transactions.results;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.access.*;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import com.djrapitops.plan.extension.table.Table;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable.*;
/**
* Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}.
*
* @author Rsl1122
*/
public class StorePlayerTableResultTransaction extends Transaction {
private final String pluginName;
private final UUID serverUUID;
private final String providerName;
private final UUID playerUUID;
private final Table table;
public StorePlayerTableResultTransaction(String pluginName, UUID serverUUID, String providerName, UUID playerUUID, Table table) {
this.pluginName = pluginName;
this.serverUUID = serverUUID;
this.providerName = providerName;
this.playerUUID = playerUUID;
this.table = table;
}
@Override
protected void performOperations() {
execute(storeValue());
}
private Executable storeValue() {
return connection -> {
int maxColumnSize = table.getMaxColumnSize();
if (maxColumnSize == 0) {
return false;
}
Integer tableID = query(tableID());
deleteOldValues(tableID).execute(connection);
insertNewValues(tableID).execute(connection);
return false;
};
}
private Executable deleteOldValues(int tableID) {
String sql = "DELETE FROM " + TABLE_NAME +
WHERE + TABLE_ID + "=?" +
AND + USER_UUID + "=?";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, tableID);
statement.setString(2, playerUUID.toString());
}
};
}
private Executable insertNewValues(int tableID) {
String sql = "INSERT INTO " + TABLE_NAME + '(' +
TABLE_ID + ',' +
USER_UUID + ',' +
VALUE_1 + ',' +
VALUE_2 + ',' +
VALUE_3 + ',' +
VALUE_4 +
") VALUES (?,?,?,?,?,?)";
return new ExecBatchStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
int maxColumnSize = Math.min(table.getMaxColumnSize(), 4); // Limit to maximum 4 columns, or how many column names there are.
for (Object[] row : table.getRows()) {
statement.setInt(1, tableID);
statement.setString(2, playerUUID.toString());
for (int i = 0; i < maxColumnSize; i++) {
Object value = row[i];
setStringOrNull(statement, 3 + i, value != null ? value.toString() : null);
}
// Rest are set null if not 4 columns wide.
for (int i = maxColumnSize; i < 4; i++) {
statement.setNull(3 + i, Types.VARCHAR);
}
statement.addBatch();
}
}
};
}
private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
if (value != null) {
statement.setString(index, value);
} else {
statement.setNull(index, Types.VARCHAR);
}
}
private Query<Integer> tableID() {
String sql = SELECT + ExtensionTableProviderTable.ID +
FROM + ExtensionTableProviderTable.TABLE_NAME +
WHERE + ExtensionTableProviderTable.PROVIDER_NAME + "=?" +
AND + ExtensionTableProviderTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
" LIMIT 1";
return new QueryStatement<Integer>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, providerName, pluginName, serverUUID);
}
@Override
public Integer processResults(ResultSet set) throws SQLException {
if (set.next()) {
int id = set.getInt(ExtensionTableProviderTable.ID);
if (!set.wasNull()) {
return id;
}
}
throw new DBOpException("Table Provider was not saved before storing results. Please report this issue. Extension method: " + pluginName + "#" + providerName);
}
};
}
}

View File

@ -0,0 +1,154 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.transactions.results;
import com.djrapitops.plan.api.exceptions.database.DBOpException;
import com.djrapitops.plan.db.access.*;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable;
import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable;
import com.djrapitops.plan.extension.table.Table;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;
import static com.djrapitops.plan.db.sql.parsing.Sql.*;
import static com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable.*;
/**
* Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}.
*
* @author Rsl1122
*/
public class StoreServerTableResultTransaction extends Transaction {
private final String pluginName;
private final UUID serverUUID;
private final String providerName;
private final Table table;
public StoreServerTableResultTransaction(String pluginName, UUID serverUUID, String providerName, Table table) {
this.pluginName = pluginName;
this.serverUUID = serverUUID;
this.providerName = providerName;
this.table = table;
}
@Override
protected void performOperations() {
execute(storeValue());
}
private Executable storeValue() {
return connection -> {
int maxColumnSize = table.getMaxColumnSize();
if (maxColumnSize == 0) {
return false;
}
Integer tableID = query(tableID());
deleteOldValues(tableID).execute(connection);
insertNewValues(tableID).execute(connection);
return false;
};
}
private Executable deleteOldValues(int tableID) {
String sql = "DELETE FROM " + TABLE_NAME +
WHERE + TABLE_ID + "=?" +
AND + SERVER_UUID + "=?";
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, tableID);
statement.setString(2, serverUUID.toString());
}
};
}
private Executable insertNewValues(int tableID) {
String sql = "INSERT INTO " + TABLE_NAME + '(' +
TABLE_ID + ',' +
SERVER_UUID + ',' +
VALUE_1 + ',' +
VALUE_2 + ',' +
VALUE_3 + ',' +
VALUE_4 + ',' +
VALUE_5 +
") VALUES (?,?,?,?,?,?, ?)";
return new ExecBatchStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
int maxColumnSize = Math.min(table.getMaxColumnSize(), 5); // Limit to maximum 5 columns, or how many column names there are.
for (Object[] row : table.getRows()) {
statement.setInt(1, tableID);
statement.setString(2, serverUUID.toString());
for (int i = 0; i < maxColumnSize; i++) {
Object value = row[i];
setStringOrNull(statement, 3 + i, value != null ? value.toString() : null);
}
// Rest are set null if not 5 columns wide.
for (int i = maxColumnSize; i < 5; i++) {
statement.setNull(3 + i, Types.VARCHAR);
}
statement.addBatch();
}
}
};
}
private void setStringOrNull(PreparedStatement statement, int index, String value) throws SQLException {
if (value != null) {
statement.setString(index, value);
} else {
statement.setNull(index, Types.VARCHAR);
}
}
private Query<Integer> tableID() {
String sql = SELECT + ExtensionTableProviderTable.ID +
FROM + ExtensionTableProviderTable.TABLE_NAME +
WHERE + ExtensionTableProviderTable.PROVIDER_NAME + "=?" +
AND + ExtensionTableProviderTable.PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID +
" LIMIT 1";
return new QueryStatement<Integer>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
ExtensionTableProviderTable.set3PluginValuesToStatement(statement, 1, providerName, pluginName, serverUUID);
}
@Override
public Integer processResults(ResultSet set) throws SQLException {
if (set.next()) {
int id = set.getInt(ExtensionTableProviderTable.ID);
if (!set.wasNull()) {
return id;
}
}
throw new DBOpException("Table Provider was not saved before storing results. Please report this issue. Extension method: " + pluginName + "#" + providerName);
}
};
}
}

View File

@ -0,0 +1,81 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.table;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
/**
* Utility for accessing implementation variables inside Table.Factory object.
*
* @author Rsl1122
*/
public class TableAccessor {
private TableAccessor() {
/* Static method class */
}
public static Color getColor(Table.Factory factory) {
return factory.color;
}
public static void setColor(Table.Factory factory, Color color) {
factory.color = color;
}
public static String getTableName(Table.Factory factory) {
return factory.tableName;
}
public static void setTableName(Table.Factory factory, String tableName) {
factory.tableName = tableName;
}
public static String getTabName(Table.Factory factory) {
return factory.tabName;
}
public static void setTabName(Table.Factory factory, String tabName) {
factory.tabName = tabName;
}
public static int getTabPriority(Table.Factory factory) {
return factory.tabPriority;
}
public static void setTabPriority(Table.Factory factory, int tabPriority) {
factory.tabPriority = tabPriority;
}
public static ElementOrder[] getTabOrder(Table.Factory factory) {
return factory.tabOrder;
}
public static void setTabOrder(Table.Factory factory, ElementOrder[] tabOrder) {
factory.tabOrder = tabOrder;
}
public static Icon getTabIcon(Table.Factory factory) {
return factory.tabIcon;
}
public static void setTabIcon(Table.Factory factory, Icon tabIcon) {
factory.tabIcon = tabIcon;
}
}

View File

@ -58,6 +58,9 @@ public enum Color {
}
public static Optional<Color> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {

View File

@ -37,6 +37,9 @@ public enum Family {
}
public static Optional<Family> getByName(String name) {
if (name == null) {
return Optional.empty();
}
try {
return Optional.of(valueOf(name));
} catch (IllegalArgumentException e) {

View File

@ -30,6 +30,9 @@ public class Icon {
}
public static Icon fromExtensionIcon(com.djrapitops.plan.extension.icon.Icon icon) {
if (icon == null) {
return Icon.called("question").build();
}
return new Icon(
Family.getByName(icon.getFamily().name()).orElse(Family.SOLID),
icon.getName(),

View File

@ -16,11 +16,13 @@
*/
package com.djrapitops.plan.utilities.html.pages;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.FormatType;
import com.djrapitops.plan.extension.implementation.TabInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive;
import com.djrapitops.plan.extension.implementation.results.ExtensionInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import com.djrapitops.plan.extension.implementation.results.ExtensionTableData;
import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData;
import com.djrapitops.plan.utilities.formatting.Formatter;
import com.djrapitops.plan.utilities.formatting.Formatters;
@ -50,6 +52,8 @@ public class AnalysisPluginTabs {
private String nav;
private String tab;
private boolean hasWideTable;
public AnalysisPluginTabs(String nav, String tab) {
this.nav = nav;
this.tab = tab;
@ -70,6 +74,8 @@ public class AnalysisPluginTabs {
this.decimalFormatter = formatters.decimals();
this.percentageFormatter = formatters.percentage();
hasWideTable = false;
generate();
}
@ -106,7 +112,7 @@ public class AnalysisPluginTabs {
String tabsElement;
if (onlyGeneric) {
ExtensionTabData genericTabData = datum.getTabs().get(0);
tabsElement = Html.BODY.parse(parseDataHtml(genericTabData));
tabsElement = parseContentHtml(genericTabData);
} else {
tabsElement = new TabsElement(
datum.getTabs().stream().map(this::wrapToTabElementTab).toArray(TabsElement.Tab[]::new)
@ -125,11 +131,53 @@ public class AnalysisPluginTabs {
private TabsElement.Tab wrapToTabElementTab(ExtensionTabData tabData) {
TabInformation tabInformation = tabData.getTabInformation();
String tabContentHtml = parseContentHtml(tabData);
return new TabsElement.Tab(tabInformation.getTabName(), parseDataHtml(tabData));
String tabName = tabInformation.getTabName();
return new TabsElement.Tab(tabName.isEmpty()
? Icon.called("info-circle").build().toHtml() + " General"
: Icon.fromExtensionIcon(tabInformation.getTabIcon()).toHtml() + ' ' + tabName,
tabContentHtml);
}
private String parseDataHtml(ExtensionTabData tabData) {
private String parseContentHtml(ExtensionTabData tabData) {
TabInformation tabInformation = tabData.getTabInformation();
ElementOrder[] order = tabInformation.getTabElementOrder().orElse(ElementOrder.values());
String values = parseValuesHtml(tabData);
String valuesHtml = values.isEmpty() ? "" : Html.BODY.parse(values);
String tablesHtml = parseTablesHtml(tabData);
StringBuilder builder = new StringBuilder();
for (ElementOrder ordering : order) {
switch (ordering) {
case VALUES:
builder.append(valuesHtml);
break;
case TABLE:
builder.append(tablesHtml);
break;
default:
break;
}
}
return builder.toString();
}
private String parseTablesHtml(ExtensionTabData tabData) {
StringBuilder builder = new StringBuilder();
for (ExtensionTableData tableData : tabData.getTableData()) {
if (tableData.isWideTable()) {
hasWideTable = true;
}
builder.append(tableData.getHtmlTable().parseHtml());
}
return builder.toString();
}
private String parseValuesHtml(ExtensionTabData tabData) {
StringBuilder builder = new StringBuilder();
for (String key : tabData.getValueOrder()) {
tabData.getBoolean(key).ifPresent(data -> append(builder, data.getDescriptive(), data.getFormattedValue()));
@ -153,7 +201,8 @@ public class AnalysisPluginTabs {
}
private String wrapInContainer(ExtensionInformation information, String tabsElement) {
return "<div class=\"col-xs-12 col-sm-12 col-md-4 col-lg-4\"><div class=\"card\">" +
String colWidth = hasWideTable ? "col-md-8 col-lg-8" : "col-md-4 col-lg-4";
return "<div class=\"col-xs-12 col-sm-12 " + colWidth + "\"><div class=\"card\">" +
"<div class=\"header\">" +
"<h2>" + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "</h2>" +
"</div>" +

View File

@ -16,11 +16,13 @@
*/
package com.djrapitops.plan.utilities.html.pages;
import com.djrapitops.plan.extension.ElementOrder;
import com.djrapitops.plan.extension.FormatType;
import com.djrapitops.plan.extension.implementation.TabInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive;
import com.djrapitops.plan.extension.implementation.results.ExtensionInformation;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import com.djrapitops.plan.extension.implementation.results.ExtensionTableData;
import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData;
import com.djrapitops.plan.utilities.formatting.Formatter;
import com.djrapitops.plan.utilities.formatting.Formatters;
@ -48,6 +50,8 @@ public class InspectPluginTab implements Comparable<InspectPluginTab> {
private String nav;
private String tab;
private boolean hasWideTable;
public InspectPluginTab(String nav, String tab) {
this.nav = nav;
this.tab = tab;
@ -70,6 +74,8 @@ public class InspectPluginTab implements Comparable<InspectPluginTab> {
this.decimalFormatter = formatters.decimals();
this.percentageFormatter = formatters.percentage();
hasWideTable = false;
generate();
}
@ -106,7 +112,7 @@ public class InspectPluginTab implements Comparable<InspectPluginTab> {
String tabsElement;
if (onlyGeneric) {
ExtensionTabData genericTabData = datum.getTabs().get(0);
tabsElement = Html.BODY.parse(parseDataHtml(genericTabData));
tabsElement = parseContentHtml(genericTabData);
} else {
tabsElement = new TabsElement(
datum.getTabs().stream().map(this::wrapToTabElementTab).toArray(TabsElement.Tab[]::new)
@ -125,11 +131,53 @@ public class InspectPluginTab implements Comparable<InspectPluginTab> {
private TabsElement.Tab wrapToTabElementTab(ExtensionTabData tabData) {
TabInformation tabInformation = tabData.getTabInformation();
String tabContentHtml = parseContentHtml(tabData);
return new TabsElement.Tab(tabInformation.getTabName(), parseDataHtml(tabData));
String tabName = tabInformation.getTabName();
return new TabsElement.Tab(tabName.isEmpty()
? Icon.called("info-circle").build().toHtml() + " General"
: Icon.fromExtensionIcon(tabInformation.getTabIcon()).toHtml() + ' ' + tabName,
tabContentHtml);
}
private String parseDataHtml(ExtensionTabData tabData) {
private String parseContentHtml(ExtensionTabData tabData) {
TabInformation tabInformation = tabData.getTabInformation();
ElementOrder[] order = tabInformation.getTabElementOrder().orElse(ElementOrder.values());
String values = parseValuesHtml(tabData);
String valuesHtml = values.isEmpty() ? "" : Html.BODY.parse(values);
String tablesHtml = parseTablesHtml(tabData);
StringBuilder builder = new StringBuilder();
for (ElementOrder ordering : order) {
switch (ordering) {
case VALUES:
builder.append(valuesHtml);
break;
case TABLE:
builder.append(tablesHtml);
break;
default:
break;
}
}
return builder.toString();
}
private String parseTablesHtml(ExtensionTabData tabData) {
StringBuilder builder = new StringBuilder();
for (ExtensionTableData tableData : tabData.getTableData()) {
if (tableData.isWideTable()) {
hasWideTable = true;
}
builder.append(tableData.getHtmlTable().parseHtml());
}
return builder.toString();
}
private String parseValuesHtml(ExtensionTabData tabData) {
StringBuilder builder = new StringBuilder();
for (String key : tabData.getValueOrder()) {
tabData.getBoolean(key).ifPresent(data -> append(builder, data.getDescriptive(), data.getFormattedValue()));
@ -153,7 +201,8 @@ public class InspectPluginTab implements Comparable<InspectPluginTab> {
}
private String wrapInContainer(ExtensionInformation information, String tabsElement) {
return "<div class=\"col-xs-12 col-sm-12 col-md-4 col-lg-4\"><div class=\"card\">" +
String colWidth = hasWideTable ? "col-md-8 col-lg-8" : "col-md-4 col-lg-4";
return "<div class=\"col-xs-12 col-sm-12 " + colWidth + "\"><div class=\"card\">" +
"<div class=\"header\">" +
"<h2>" + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "</h2>" +
"</div>" +

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.db;
import com.djrapitops.plan.data.WebUser;
import com.djrapitops.plan.data.container.*;
import com.djrapitops.plan.data.element.TableContainer;
import com.djrapitops.plan.data.store.Key;
import com.djrapitops.plan.data.store.containers.AnalysisContainer;
import com.djrapitops.plan.data.store.containers.NetworkContainer;
@ -49,16 +50,21 @@ import com.djrapitops.plan.db.patches.Patch;
import com.djrapitops.plan.db.tasks.DBCleanTask;
import com.djrapitops.plan.extension.CallEvents;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.ExtensionService;
import com.djrapitops.plan.extension.ExtensionServiceImplementation;
import com.djrapitops.plan.extension.annotation.*;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.results.ExtensionBooleanData;
import com.djrapitops.plan.extension.implementation.results.ExtensionStringData;
import com.djrapitops.plan.extension.implementation.results.ExtensionTabData;
import com.djrapitops.plan.extension.implementation.results.ExtensionTableData;
import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData;
import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionPlayerDataQuery;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerDataQuery;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalResultsTransaction;
import com.djrapitops.plan.extension.table.Table;
import com.djrapitops.plan.system.PlanSystem;
import com.djrapitops.plan.system.database.DBSystem;
import com.djrapitops.plan.system.info.server.Server;
@ -160,9 +166,11 @@ public abstract class CommonDBTest {
db.executeTransaction(new StoreServerInformationTransaction(new Server(-1, serverUUID, "ServerName", "", 20)));
assertEquals(serverUUID, db.getServerUUIDSupplier().get());
system.getExtensionService().unregister(new PlayerExtension());
system.getExtensionService().unregister(new ServerExtension());
system.getExtensionService().unregister(new ConditionalExtension());
ExtensionService extensionService = system.getExtensionService();
extensionService.unregister(new PlayerExtension());
extensionService.unregister(new ServerExtension());
extensionService.unregister(new ConditionalExtension());
extensionService.unregister(new TableExtension());
}
private void execute(Executable executable) {
@ -1155,7 +1163,7 @@ public abstract class CommonDBTest {
ExtensionServiceImplementation extensionService = (ExtensionServiceImplementation) system.getExtensionService();
extensionService.register(new PlayerExtension());
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.PLAYER_JOIN);
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.MANUAL);
Map<UUID, List<ExtensionPlayerData>> playerDataByServerUUID = db.query(new ExtensionPlayerDataQuery(playerUUID));
List<ExtensionPlayerData> ofServer = playerDataByServerUUID.get(serverUUID);
@ -1201,7 +1209,7 @@ public abstract class CommonDBTest {
ExtensionServiceImplementation extensionService = (ExtensionServiceImplementation) system.getExtensionService();
extensionService.register(new PlayerExtension());
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.PLAYER_JOIN);
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.MANUAL);
List<ExtensionServerData> ofServer = db.query(new ExtensionServerDataQuery(serverUUID));
assertFalse(ofServer.isEmpty());
@ -1228,14 +1236,14 @@ public abstract class CommonDBTest {
extensionService.register(new ConditionalExtension());
ConditionalExtension.condition = true;
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.PLAYER_JOIN);
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.MANUAL);
// Check that the wanted data exists
checkThatDataExists(ConditionalExtension.condition);
// Reverse condition
ConditionalExtension.condition = false;
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.PLAYER_JOIN);
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.MANUAL);
db.executeTransaction(new RemoveUnsatisfiedConditionalResultsTransaction());
@ -1244,7 +1252,7 @@ public abstract class CommonDBTest {
// Reverse condition
ConditionalExtension.condition = false;
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.PLAYER_JOIN);
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.MANUAL);
db.executeTransaction(new RemoveUnsatisfiedConditionalResultsTransaction());
@ -1273,6 +1281,70 @@ public abstract class CommonDBTest {
}
}
@Test
public void extensionServerTableValuesAreInserted() {
ExtensionServiceImplementation extensionService = (ExtensionServiceImplementation) system.getExtensionService();
extensionService.register(new TableExtension());
extensionService.updateServerValues(CallEvents.MANUAL);
extensionService.updateServerValues(CallEvents.MANUAL);
List<ExtensionServerData> ofServer = db.query(new ExtensionServerDataQuery(serverUUID));
assertFalse(ofServer.isEmpty());
ExtensionServerData extensionServerData = ofServer.get(0);
List<ExtensionTabData> tabs = extensionServerData.getTabs();
assertEquals(1, tabs.size()); // No tab defined, should contain 1 tab
ExtensionTabData tabData = tabs.get(0);
List<ExtensionTableData> tableData = tabData.getTableData();
assertEquals(1, tableData.size());
ExtensionTableData table = tableData.get(0);
TableContainer expected = new TableContainer(
"<i class=\" fa fa-gavel\"></i> first",
"<i class=\" fa fa-what\"></i> second",
"<i class=\" fa fa-question\"></i> third"
);
expected.setColor("amber");
expected.addRow("value", 3, 0.5, 400L);
assertEquals(expected.parseHtml(), table.getHtmlTable().parseHtml());
}
@Test
public void extensionPlayerTableValuesAreInserted() {
ExtensionServiceImplementation extensionService = (ExtensionServiceImplementation) system.getExtensionService();
extensionService.register(new TableExtension());
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.MANUAL);
extensionService.updatePlayerValues(playerUUID, TestConstants.PLAYER_ONE_NAME, CallEvents.MANUAL);
Map<UUID, List<ExtensionPlayerData>> ofPlayer = db.query(new ExtensionPlayerDataQuery(playerUUID));
assertFalse(ofPlayer.isEmpty());
List<ExtensionPlayerData> ofServer = ofPlayer.get(serverUUID);
assertEquals(1, ofServer.size());
ExtensionPlayerData extensionServerData = ofServer.get(0);
List<ExtensionTabData> tabs = extensionServerData.getTabs();
assertEquals(1, tabs.size()); // No tab defined, should contain 1 tab
ExtensionTabData tabData = tabs.get(0);
List<ExtensionTableData> tableData = tabData.getTableData();
assertEquals(1, tableData.size());
ExtensionTableData table = tableData.get(0);
TableContainer expected = new TableContainer(
"<i class=\" fa fa-gavel\"></i> first",
"<i class=\" fa fa-what\"></i> second",
"<i class=\" fa fa-question\"></i> third"
);
expected.setColor("amber");
expected.addRow("value", 3, 0.5, 400L);
assertEquals(expected.parseHtml(), table.getHtmlTable().parseHtml());
}
@PluginInfo(name = "ConditionalExtension")
public static class ConditionalExtension implements DataExtension {
@ -1356,4 +1428,27 @@ public abstract class CommonDBTest {
return "Something";
}
}
@PluginInfo(name = "TableExtension")
public class TableExtension implements DataExtension {
@TableProvider(tableColor = Color.AMBER)
public Table table() {
return createTestTable();
}
@TableProvider(tableColor = Color.AMBER)
public Table playerTable(UUID playerUUID) {
return createTestTable();
}
private Table createTestTable() {
return Table.builder()
.columnOne("first", Icon.called("gavel").of(Color.AMBER).build())
.columnTwo("second", Icon.called("what").of(Color.BROWN).build()) // Colors are ignored
.columnThree("third", null) // Can handle improper icons
.columnFive("five", Icon.called("").build()) // Can handle null column in between and ignore the next column
.addRow("value", 3, 0.5, 400L) // Can handle too many row values
.build();
}
}
}