TabooMan!

master
坏黑 2018-05-07 23:48:22 +08:00
commit 8c6518ffc5
24 changed files with 951 additions and 22 deletions

21
.gitignore vendored
View File

@ -20,7 +20,20 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
.gradle/4.3.1/
.idea/TabooLib.iml
.idea/misc.xml
.idea/modules.xml
.idea/workspace.xml
.idea
target/TabooLib-3.832-shaded.jar
target/TabooLib-3.832.jar
target/classes/JavaShells/
target/classes/Language/
target/classes/Language2/
target/classes/TLM/
target/classes/config.yml
target/classes/internalLang.yml
target/classes/items.yml
target/classes/lang/
target/classes/module.yml
target/classes/plugin.yml
target/generated-sources/
target/maven-archiver/
target/maven-status/
target/original-TabooLib-3.832.jar

View File

@ -10,6 +10,7 @@
<inspection_tool class="AlibabaClassNamingShouldBeCamel" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="AlibabaConstantFieldShouldBeUpperCase" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaEnumConstantsMustHaveComment" enabled="false" level="CRITICAL" enabled_by_default="false" />
<inspection_tool class="AlibabaThreadPoolCreation" enabled="false" level="BLOCKER" enabled_by_default="false" />
<inspection_tool class="AlibabaUndefineMagicConstant" enabled="false" level="MAJOR" enabled_by_default="false" />
<inspection_tool class="JavaDoc" enabled="false" level="WARNING" enabled_by_default="false">
<option name="TOP_LEVEL_CLASS_OPTIONS">

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: com.h2database:h2:1.4.197">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/h2database/h2/1.4.197/h2-1.4.197.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/h2database/h2/1.4.197/h2-1.4.197-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/h2database/h2/1.4.197/h2-1.4.197-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: javax.servlet:servlet-api:2.5">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/javax/servlet/servlet-api/2.5/servlet-api-2.5-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/javax/servlet/servlet-api/2.5/servlet-api-2.5-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.codehaus.jackson:jackson-core-asl:1.9.13">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/codehaus/jackson/jackson-core-asl/1.9.13/jackson-core-asl-1.9.13.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/codehaus/jackson/jackson-core-asl/1.9.13/jackson-core-asl-1.9.13-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/codehaus/jackson/jackson-core-asl/1.9.13/jackson-core-asl-1.9.13-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.codehaus.jackson:jackson-mapper-asl:1.9.13">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/codehaus/jackson/jackson-mapper-asl/1.9.13/jackson-mapper-asl-1.9.13.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/codehaus/jackson/jackson-mapper-asl/1.9.13/jackson-mapper-asl-1.9.13-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/codehaus/jackson/jackson-mapper-asl/1.9.13/jackson-mapper-asl-1.9.13-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.javalite:activejdbc:2.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/javalite/activejdbc/2.0/activejdbc-2.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/javalite/activejdbc/2.0/activejdbc-2.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/javalite/activejdbc/2.0/activejdbc-2.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.javalite:app-config:2.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/javalite/app-config/2.0/app-config-2.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/javalite/app-config/2.0/app-config-2.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/javalite/app-config/2.0/app-config-2.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.javalite:javalite-common:2.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/javalite/javalite-common/2.0/javalite-common-2.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/javalite/javalite-common/2.0/javalite-common-2.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/javalite/javalite-common/2.0/javalite-common-2.0-sources.jar!/" />
</SOURCES>
</library>
</component>

16
pom.xml
View File

@ -67,6 +67,22 @@
<artifactId>HikariCP</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.javalite</groupId>
<artifactId>activejdbc</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
</dependency>
<dependency>
<groupId>com.ilummc.eagletdl</groupId>
<artifactId>EagletCore</artifactId>

View File

@ -3,6 +3,7 @@ package com.ilummc.tlib;
import com.ilummc.tlib.annotations.Dependency;
import com.ilummc.tlib.compat.PlaceholderHook;
import com.ilummc.tlib.config.TLibConfig;
import com.ilummc.tlib.db.Pool;
import com.ilummc.tlib.filter.TLoggerFilter;
import com.ilummc.tlib.inject.TConfigWatcher;
import com.ilummc.tlib.inject.TDependencyInjector;
@ -24,6 +25,16 @@ import java.nio.charset.Charset;
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.ow2.asm:asm:6.1.1")
@Dependency(type = Dependency.Type.LIBRARY, maven = "com.zaxxer:HikariCP:3.1.0")
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.slf4j:slf4j-api:1.7.25")
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.javalite:activejdbc:2.0")
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.javalite:javalite-common:2.0")
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.javalite:app-config:2.0")
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.codehaus.jackson:jackson-mapper-asl:1.9.13")
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.codehaus.jackson:jackson-core-asl:1.9.13")
@Dependency(type = Dependency.Type.LIBRARY, maven = "jaxen:jaxen:1.1.6")
@Dependency(type = Dependency.Type.LIBRARY, maven = "dom4j:dom4j:1.6.1")
@Dependency(type = Dependency.Type.LIBRARY, maven = "xml-apis:xml-apis:1.0.b2")
@Dependency(type = Dependency.Type.LIBRARY, maven = "org.ehcache:ehcache:3.5.2")
@Dependency(type = Dependency.Type.LIBRARY, maven = "com.h2database:h2:1.4.197")
public class TLib {
private static TLib tLib;
@ -60,10 +71,20 @@ public class TLib {
TLoggerFilter.init();
TLocaleLoader.init();
PlaceholderHook.init();
TLocaleLoader.load(Main.getInst(), false);
TDependencyInjector.inject(Main.getInst(), tLib);
// init database
try {
Pool.init();
} catch (Throwable e) {
e.printStackTrace();
}
}
public static void unload() {
Pool.unload();
tLib.getConfigWatcher().unregisterAll();
TDependencyInjector.eject(Main.getInst(), tLib);
}

View File

@ -9,13 +9,28 @@ import com.ilummc.tlib.annotations.TConfig;
@TConfig(name = "tlib.yml", listenChanges = true)
public class TLibConfig {
private boolean enablePlaceholderHookByDefault = false;
@Getter
private String dataSourceClassName;
public void setEnablePlaceholderHookByDefault(boolean enablePlaceholderHookByDefault) {
this.enablePlaceholderHookByDefault = enablePlaceholderHookByDefault;
}
@Getter
private String jdbcUrl = "jdbc:h2:file:~/plugins/TabooLib/h2";
@Getter
private String driverClassName;
@Getter
private String username = "";
@Getter
private String password = "";
@Getter
private int maximumPoolSize = 4;
@Getter
private Map<String, Object> settings = new HashMap<String, Object>() {{
put("cachePrepStmts", true);
put("useServerPrepStmts", true);
}};
public boolean isEnablePlaceholderHookByDefault() {
return enablePlaceholderHookByDefault;
}
}

View File

@ -0,0 +1,61 @@
package com.ilummc.tlib.db;
import com.ilummc.tlib.TLib;
import com.ilummc.tlib.resources.TLocale;
import org.javalite.activejdbc.Base;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public final class Pool extends ThreadPoolExecutor {
private static final AtomicInteger number = new AtomicInteger(1);
private static final Pool singleton = new Pool();
private final TLibDataSource dataSource;
private Pool() {
super(TLib.getTLib().getConfig().getMaximumPoolSize(),
TLib.getTLib().getConfig().getMaximumPoolSize(),
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
try {
dataSource = new TLibDataSource();
this.setThreadFactory(r -> new Thread(() -> {
Base.open(dataSource.getDataSource());
r.run();
}, "TabooLib-DbPool-" + number.getAndIncrement()));
prestartAllCoreThreads();
TLocale.sendToConsole("DATABASE.CONNECTION-ESTABLISHED", dataSource.getDataSource().getConnection().getMetaData().getDatabaseProductName(),
String.valueOf(TLib.getTLib().getConfig().getMaximumPoolSize()));
} catch (Exception e) {
TLocale.sendToConsole("DATABASE.CONNECTION-ERROR", e.toString());
throw new RuntimeException();
}
}
public static void run(Runnable runnable) {
instance().execute(runnable);
}
public static void init() {
}
public static void unload() {
instance().dataSource.disconnect();
instance().shutdown();
}
public static Pool instance() {
return singleton;
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
if (t != null) Base.close();
}
}

View File

@ -0,0 +1,35 @@
package com.ilummc.tlib.db;
import com.ilummc.tlib.TLib;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.javalite.activejdbc.Base;
import javax.sql.DataSource;
import java.util.Properties;
public class TLibDataSource {
private final HikariDataSource dataSource;
TLibDataSource() {
Properties properties = new Properties();
properties.put("jdbcUrl", TLib.getTLib().getConfig().getJdbcUrl());
properties.put("username", TLib.getTLib().getConfig().getUsername());
properties.put("password", TLib.getTLib().getConfig().getPassword());
properties.put("dataSourceClassName", TLib.getTLib().getConfig().getDataSourceClassName());
properties.put("driverClassName", TLib.getTLib().getConfig().getDriverClassName());
TLib.getTLib().getConfig().getSettings().forEach((k, v) -> properties.put("dataSource." + k, v));
dataSource = new HikariDataSource(new HikariConfig(properties));
Base.open(dataSource);
}
public DataSource getDataSource() {
return dataSource;
}
public void disconnect() {
Base.close();
}
}

View File

@ -1,6 +1,8 @@
package com.ilummc.tlib.inject;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ilummc.tlib.annotations.TConfig;
import com.ilummc.tlib.resources.TLocale;
import me.skymc.taboolib.fileutils.ConfigUtils;
@ -8,12 +10,12 @@ import org.apache.commons.lang3.Validate;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
public class TConfigInjector {
@ -106,7 +108,7 @@ public class TConfigInjector {
try {
TConfig config = object.getClass().getAnnotation(TConfig.class);
Validate.notNull(config);
return ConfigUtils.objToConf(object).getValues(false);
return ConfigUtils.objToMap(ConfigUtils.objToConf(object).getValues(false), config.excludeModifiers());
} catch (NullPointerException e) {
TLocale.Logger.warn("CONFIG.SAVE-FAIL-NO-ANNOTATION", plugin.toString(), object.getClass().getSimpleName());
} catch (Exception e) {
@ -118,16 +120,12 @@ public class TConfigInjector {
public static void saveConfig(Plugin plugin, Object object) throws IOException, NullPointerException {
TConfig config = object.getClass().getAnnotation(TConfig.class);
Validate.notNull(config);
Object obj = serialize(plugin, object);
Validate.notNull(obj);
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
Map map = gson.fromJson(gson.toJson(object), HashMap.class);
YamlConfiguration configuration = (YamlConfiguration) ConfigUtils.mapToConf(map);
File target = new File(plugin.getDataFolder(), config.name());
if (!target.exists()) target.createNewFile();
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setAllowUnicode(false);
Yaml yaml = new Yaml(options);
String str = yaml.dump(obj);
byte[] arr = str.getBytes(config.charset());
byte[] arr = configuration.saveToString().getBytes(config.charset());
Files.write(arr, target);
}

View File

@ -0,0 +1,9 @@
package me.skymc.taboolib.commands.internal;
/**
* @Author sky
* @Since 2018-05-07 21:36
*/
public class InternalMainCommand {
}

View File

@ -0,0 +1,9 @@
package me.skymc.taboolib.commands.internal;
/**
* @Author sky
* @Since 2018-05-07 21:38
*/
public class InternalCommandExecutor {
}

View File

@ -0,0 +1,20 @@
package me.skymc.taboolib.commands.internal;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
/**
* @author Bkm016
* @since 2018-04-17
*/
public interface InternalCommand {
String getLabel();
String getDescription();
InternalCommandArgument[] getArguments();
void onCommand(CommandSender sender, Command command, String label, String[] args);
}

View File

@ -0,0 +1,51 @@
package me.skymc.taboolib.commands.internal;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* @author Bkm016
* @since 2018-04-17
*/
public abstract class InternalCommandExecutor implements InternalCommand {
public InternalCommandType getType() {
return InternalCommandType.ALL;
}
public boolean requiredPlayer() {
return false;
}
public boolean isParameterConform(String[] args) {
return IntStream.range(0, getArguments().length).noneMatch(i -> getArguments()[i].isRequired() && (args == null || args.length <= i));
}
public boolean isConfirmType(CommandSender sender, InternalCommandType commandType) {
return commandType == InternalCommandType.ALL || sender instanceof ConsoleCommandSender && commandType == InternalCommandType.CONSOLE;
}
public List<String> getTabCompleter(List<InternalCommandExecutor> internalCommandExecutors, String[] args) {
return args.length == 1 ? internalCommandExecutors.stream().filter(internalCommandExecutor -> internalCommandExecutor != null && (args[0].isEmpty() || internalCommandExecutor.getLabel().startsWith(args[0]))).map(InternalCommand::getLabel).collect(Collectors.toList()) : null;
}
public String getCommandString(String label) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("§f /");
stringBuilder.append(label);
stringBuilder.append(" ");
stringBuilder.append(getLabel());
stringBuilder.append(" ");
for (InternalCommandArgument parameter : getArguments()) {
stringBuilder.append(parameter.toString());
stringBuilder.append(" ");
}
stringBuilder.append("§6- §e");
stringBuilder.append(getDescription());
return stringBuilder.toString();
}
}

View File

@ -0,0 +1,11 @@
package me.skymc.taboolib.commands.internal;
/**
* @author Bkm016
* @since 2018-04-17
*/
public enum InternalCommandType {
CONSOLE, PLAYER, ALL
}

View File

@ -0,0 +1,9 @@
package me.skymc.taboolib.commands.plugin;
/**
* @Author sky
* @Since 2018-05-07 20:14
*/
public class TabooLibPluginCommand {
}

View File

@ -68,7 +68,7 @@ public class ConfigUtils {
}
public static MemoryConfiguration mapToConf(Map<String, Object> map) {
MemoryConfiguration configuration = new MemoryConfiguration();
YamlConfiguration configuration = new YamlConfiguration();
convertMapsToSections(map, configuration);
return configuration;
}
@ -112,13 +112,24 @@ public class ConfigUtils {
return objToMap(object, Modifier.TRANSIENT & Modifier.STATIC & Ref.ACC_SYNTHETIC);
}
@SuppressWarnings("unchecked")
public static Map<String, Object> objToMap(Object object, int excludedModifiers) {
Map<String, Object> map = Maps.newHashMap();
if (object instanceof Map) {
((Map<String, ?>) object).forEach((k, v) -> map.put(k, objToMap(v, excludedModifiers)));
return map;
}
if (object instanceof ConfigurationSection) {
map.putAll(objToMap(((ConfigurationSection) object).getValues(false), excludedModifiers));
return map;
}
for (Field field : Ref.getDeclaredFields(object.getClass(), excludedModifiers, false)) {
try {
if (!field.isAccessible()) field.setAccessible(true);
Object obj = field.get(object);
if (obj instanceof Property) obj = ((Property) obj).get();
if (obj instanceof ConfigurationSection)
obj = objToMap(((ConfigurationSection) obj).getValues(false), excludedModifiers);
map.put(Ref.getSerializedName(field), obj);
} catch (IllegalAccessException ignored) {
}

View File

@ -0,0 +1,49 @@
package me.skymc.taboolib.string;
import java.util.ArrayList;
import java.util.List;
/**
* @author Bkm016
* @since 2018-04-16
*/
public class ArrayUtils {
@SuppressWarnings("unchecked")
public static <T> List<T> asList(T... args) {
List<T> list = new ArrayList<>();
for (T value : args) {
list.add(value);
}
return list;
}
public static String[] addFirst(String[] args, String... value) {
if (args.length < 1) {
return value;
}
List<String> list = asList(args);
for (int i = value.length - 1 ; i >= 0 ; i--) {
list.add(0, value[i]);
}
return list.toArray(new String[0]);
}
public static String[] removeFirst(String[] args) {
if (args.length <= 1) {
return null;
}
List<String> list = asList(args);
list.remove(0);
return list.toArray(new String[0]);
}
public static String arrayJoin(String[] args, int start) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = start ; i < args.length ; i++) {
stringBuilder.append(args[i]);
stringBuilder.append(" ");
}
return stringBuilder.toString().trim();
}
}

View File

@ -0,0 +1,509 @@
/*
Copyright 2009-2018 Igor Polevoy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.javalite.activejdbc;
import org.bukkit.plugin.Plugin;
import org.javalite.activejdbc.annotations.*;
import org.javalite.activejdbc.associations.Many2ManyAssociation;
import org.javalite.activejdbc.associations.OneToManyAssociation;
import org.javalite.activejdbc.associations.OneToManyPolymorphicAssociation;
import org.javalite.activejdbc.dialects.Dialect;
import org.javalite.activejdbc.logging.LogFilter;
import org.javalite.activejdbc.logging.LogLevel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.Map.Entry;
import static org.javalite.common.Inflector.singularize;
import static org.javalite.common.Inflector.tableize;
public class MetaModel implements Serializable {
private static final Logger LOGGER = LoggerFactory.getLogger(MetaModel.class);
private static final ThreadLocal<HashMap<Class, String>> shardingTableNamesTL = new ThreadLocal<>();
private final List<Association> associations = new ArrayList<>();
private final String idName;
private final String[] compositeKeys;
private final String tableName, dbType, dbName;
private final Class<? extends Model> modelClass;
private final boolean cached;
private final String idGeneratorCode;
private final String versionColumn;
private Map<String, ColumnMetadata> columnMetadata;
private Set<String> attributeNamesNoId;
private String[] partitionIDs = null;
protected MetaModel(String dbName, Class<? extends Model> modelClass, String dbType) {
this.modelClass = modelClass;
this.idName = findIdName(modelClass);
this.compositeKeys = findCompositeKeys(modelClass);
this.tableName = findTableName(modelClass);
this.dbType = dbType;
this.cached = isCached(modelClass);
this.dbName = dbName;
this.idGeneratorCode = findIdGeneratorCode(modelClass);
this.versionColumn = findVersionColumn(modelClass);
this.partitionIDs = findPartitionIDs();
}
static Map<Class, String> getTableNamesMap() {
if (shardingTableNamesTL.get() == null)
shardingTableNamesTL.set(new HashMap<>());
return shardingTableNamesTL.get();
}
protected static String getDbName(Class<? extends Model> modelClass) {
DbName dbNameAnnotation = modelClass.getAnnotation(DbName.class);
return dbNameAnnotation == null ? DB.DEFAULT_NAME : dbNameAnnotation.value();
}
private String[] findPartitionIDs() {
PartitionIDs partitionIDs = modelClass.getAnnotation(PartitionIDs.class);
return partitionIDs != null ? partitionIDs.value() : null;
}
public boolean hasPartitionIDs() {
return partitionIDs != null;
}
public String[] getPartitionIDs() {
return partitionIDs;
}
/**
* <p>
* <strong>This feature is for sharding!</strong>
* <br>
* Do not use it to set table names <em>willy-nilly</em>!
* </p>
*
* <p>
* Sets a table name for this model. The table name is attached to a current thread and will remain there
* until it is set with a different value or cleared with {@link #clearShardTableName()} method.
* Table name set with this method overrides a table name naturally mapped to this model.
* </p>
* <p>
* Method {@link #getTableName()} will return this value for all operations related to this table.
* </p>
*
* @param tableName name of a table this model will read from current thread.
*/
public void setShardTableName(String tableName) {
getTableNamesMap().put(modelClass, tableName);
}
/**
* Clears sharding name of table attached to current thread.
* The name was supposedly attached by the {@link #setShardTableName(String)}
* method. After execution of this class, the method {@link #getTableName()} will be
* returning the value this {@link MetaModel} was initialized with during teh bootstrap phase.
*/
public void clearShardTableName() {
getTableNamesMap().remove(modelClass);
}
private boolean isCached(Class<? extends Model> modelClass) {
return null != modelClass.getAnnotation(Cached.class);
}
private String findIdName(Class<? extends Model> modelClass) {
IdName idNameAnnotation = modelClass.getAnnotation(IdName.class);
return idNameAnnotation == null ? "id" : idNameAnnotation.value();
}
private String[] findCompositeKeys(Class<? extends Model> modelClass) {
CompositePK compositeKeysAnnotation = modelClass.getAnnotation(CompositePK.class);
return compositeKeysAnnotation == null ? null : compositeKeysAnnotation.value();
}
/**
* Table name with plugin name prefix
*
* @return table name
*/
private String findTableName(Class<? extends Model> modelClass) {
Table tableAnnotation = modelClass.getAnnotation(Table.class);
String prefix = "";
try {
Field field = modelClass.getClassLoader().getClass().getDeclaredField("plugin");
field.setAccessible(true);
Plugin plugin = (Plugin) field.get(modelClass.getClassLoader());
prefix = tableize(plugin.getName()) + "_";
} catch (Throwable ignored) {
}
return prefix + (tableAnnotation == null ? tableize(modelClass.getSimpleName()) : tableAnnotation.value());
}
private String findIdGeneratorCode(Class<? extends Model> modelClass) {
IdGenerator idGenerator = modelClass.getAnnotation(IdGenerator.class);
return idGenerator == null ? null : idGenerator.value();
}
private String findVersionColumn(Class<? extends Model> modelClass) {
VersionColumn vc = modelClass.getAnnotation(VersionColumn.class);
return vc == null ? "record_version" : vc.value();
}
/**
* @return name of the column for optimistic locking record version
*/
public String getVersionColumn() {
return versionColumn;
}
public String getIdGeneratorCode() {
return idGeneratorCode;
}
public String getDbName() {
return dbName;
}
public boolean cached() {
return cached;
}
public Class<? extends Model> getModelClass() {
return modelClass;
}
/**
* Returns table name currently associated with this model.
* Table name can be modified for sharding using {@link #setShardTableName(String)}
*
* @return table name currently associated with this model.
*/
public String getTableName() {
return getTableNamesMap().getOrDefault(modelClass, tableName);
}
protected boolean tableExists() {
return columnMetadata != null && columnMetadata.isEmpty();
}
/**
* Finds all attribute names except for id.
*
* @return all attribute names except for id.
*/
public Set<String> getAttributeNamesSkipId() {
if (attributeNamesNoId == null) {//no one cares about unfortunate multi-threading timing with 2 instances created
//if someone does, use DCL with volatile
Set<String> attributesNames = new CaseInsensitiveSet(getAttributeNames());
attributesNames.remove(getIdName());
attributeNamesNoId = attributesNames;
}
return attributeNamesNoId;
}
/**
* Convenience method. Calls {@link #getAttributeNamesSkipGenerated(boolean)} and passes <code>true</code> as argument.
*
* @return list of all attributes except id, created_at, updated_at and record_version.
*/
public Set<String> getAttributeNamesSkipGenerated() {
return getAttributeNamesSkipGenerated(true);
}
/**
* Finds all attribute names except managed like <code>id</code>,
* <code>created_at</code>, <code>updated_at</code> and <code>record_version</code>, depending on argument.
*
* @param managed if true, time managed attributes <code>created_at</code> and <code>updated_at</code> will not be included (they are managed automatically).
* If false (not managed) <code>created_at</code> and <code>updated_at</code> will be included in output.
* @return list of all attributes except <code>id</code>, <code>created_at</code>, <code>updated_at</code> and
* <code>record_version</code>, depending on argument.
*/
public Set<String> getAttributeNamesSkipGenerated(boolean managed) {
//TODO: can cache this, but will need a cache for managed=true an another for managed=false
Set<String> attributesNames = new CaseInsensitiveSet(getAttributeNamesSkipId());
if (managed) {
attributesNames.remove("created_at");
attributesNames.remove("updated_at");
}
attributesNames.remove(versionColumn);
return attributesNames;
}
/**
* Finds all attribute names except those provided as arguments.
*
* @return list of all attributes except those provided as arguments.
*/
public Set<String> getAttributeNamesSkip(String... names) {
Set<String> attributes = new CaseInsensitiveSet(getAttributeNames());
for (String name : names) {
attributes.remove(name);
}
return attributes;
}
/**
* Returns true if this model supports optimistic locking, false if not
*
* @return true if this model supports optimistic locking, false if not
*/
public boolean isVersioned() {
return columnMetadata != null && columnMetadata.containsKey(versionColumn);
}
/**
* Retrieves all attribute names.
*
* @return all attribute names.
*/
protected Set<String> getAttributeNames() {
if (columnMetadata == null || columnMetadata.isEmpty())
throw new InitException("Failed to find table: " + getTableName());
return Collections.unmodifiableSet(columnMetadata.keySet());
}
public String getIdName() {
return idName;
}
/**
* Returns optional composite primary key class
*
* @return composite primary key class
*/
public String[] getCompositeKeys() {
return compositeKeys;
}
/**
* Returns association of this table with the target table. Will return null if there is no association.
*
* @param targetModelClass association of this model and the target model.
* @param associationClass class of association in requested.
* @return association of this table with the target table. Will return null if there is no association with target
* table and specified type.
*/
public <A extends Association> A getAssociationForTarget(Class<? extends Model> targetModelClass, Class<A> associationClass) {
Association result = null;
for (Association association : associations) {
if (association.getClass().equals(associationClass) && association.getTargetClass().equals(targetModelClass)) {
result = association;
break;
}
}
return (A) result;
}
/**
* Returns association of this table with the target table. Will return null if there is no association.
*
* @param targetClass association of this model and the target model.
* @return association of this table with the target table. Will return null if there is no association with target
* table and specified type.
*/
public <A extends Association> A getAssociationForTarget(Class<? extends Model> targetClass) {
Association result = null;
for (Association association : associations) {
if (association.getTargetClass().equals(targetClass)) {
result = association;
break;
}
}
return (A) result;
}
/**
* Returns associations of this table with the target table. It is possible
* to have more than one association to a target table if a target table is the same as source. Usually this
* happens when tree structures are stored in the same table (category has many categories).
*
* @param targetModelClass association of this model and the target model.
* @return list of associations of this table with the target table. Will return empty list if none found.
* table and specified type.
*/
public List<Association> getAssociationsForTarget(Class<? extends Model> targetModelClass) {
List<Association> result = new ArrayList<>();
for (Association association : associations) {
if (association.getTargetClass().equals(targetModelClass)) {
result.add(association);
}
}
return result;
}
protected void addAssociation(Association association) {
if (!associations.contains(association)) {
LogFilter.log(LOGGER, LogLevel.INFO, "Association found: {}", association);
associations.add(association);
}
}
/**
* returns true if this attribute is present in this meta model. This method i case insensitive.
*
* @param attribute attribute name, case insensitive.
* @return true if this attribute is present in this meta model, false of not.
*/
boolean hasAttribute(String attribute) {
if (columnMetadata != null) {
if (columnMetadata.containsKey(attribute)) {
return true;
} else if (attribute.startsWith("\"") && attribute.endsWith("\"")) {
return columnMetadata.containsKey(attribute.substring(1, attribute.length() - 1));
}
}
return false;
}
protected boolean hasAssociation(Class<? extends Model> targetClass, Class<? extends Association> associationClass) {
for (Association association : associations) {
if (association.getTargetClass().equals(targetClass) &&
association.getClass().equals(associationClass)) return true;
}
return false;
}
@Override
public String toString() {
final StringBuilder t = new StringBuilder();
t.append("MetaModel: ").append(tableName).append(", ").append(modelClass).append("\n");
if (columnMetadata != null) {
for (Entry<String, ColumnMetadata> metadata : columnMetadata.entrySet()) {
t.append(metadata.getValue()).append(", ");
}
}
return t.toString();
}
/**
* FK name is a foreign key name used in relationships as a foreign key column in a child table (table represented by this
* instance is a parent table).
* The FK name is derived using {@link org.javalite.common.Inflector}: It is a singular version of this table name plus "_id".
*
* @return foreign key name used in relationships as a foreign key column in a child table.
*/
public String getFKName() {
return singularize(getTableName()).toLowerCase() + "_id";
}
protected List<OneToManyAssociation> getOneToManyAssociations(List<Association> exclusions) {
List<OneToManyAssociation> one2Manies = new ArrayList<>();
for (Association association : associations) {
if (association.getClass().equals(OneToManyAssociation.class) && !exclusions.contains(association)) {
one2Manies.add((OneToManyAssociation) association);
}
}
return one2Manies;
}
protected List<OneToManyPolymorphicAssociation> getPolymorphicAssociations(List<Association> exclusions) {
List<OneToManyPolymorphicAssociation> one2Manies = new ArrayList<>();
for (Association association : associations) {
if (association.getClass().equals(OneToManyPolymorphicAssociation.class) && !exclusions.contains(association)) {
one2Manies.add((OneToManyPolymorphicAssociation) association);
}
}
return one2Manies;
}
protected List<Many2ManyAssociation> getManyToManyAssociations(List<Association> excludedAssociations) {
List<Many2ManyAssociation> many2Manies = new ArrayList<>();
for (Association association : associations) {
if (association.getClass().equals(Many2ManyAssociation.class) && !excludedAssociations.contains(association)) {
many2Manies.add((Many2ManyAssociation) association);
}
}
return many2Manies;
}
public String getDbType() {
return dbType;
}
public Dialect getDialect() {
return Registry.instance().getConfiguration().getDialect(this);
}
protected List<Association> getAssociations() {
return Collections.unmodifiableList(associations);
}
/**
* Checks if this model has a named attribute that has the same name as argument.
* <p>
* Throws <code>IllegalArgumentException</code> in case it does not find it.
*
* @param attribute name of attribute or association target.
*/
protected void checkAttribute(String attribute) {
if (!hasAttribute(attribute)) {
String sb = "Attribute: '" + attribute + "' is not defined in model: '" + getModelClass() + ". "
+ "Available attributes: " + getAttributeNames();
throw new IllegalArgumentException(sb);
}
}
/**
* Provides column metadata map, keyed by attribute names.
* Table columns correspond to ActiveJDBC model attributes.
*
* @return Provides column metadata map, keyed by attribute names.
*/
public Map<String, ColumnMetadata> getColumnMetadata() {
if (columnMetadata == null || columnMetadata.isEmpty())
throw new InitException("Failed to find table: " + getTableName());
return Collections.unmodifiableMap(columnMetadata);
}
void setColumnMetadata(Map<String, ColumnMetadata> columnMetadata) {
this.columnMetadata = columnMetadata;
}
/**
* Checks if there is association to the target model class.,
*
* @param targetModelClass class of a model that will be checked for association from current model.
* @return true if any association exists such that the current model is a source and targetModelClass is a target.
*/
public boolean isAssociatedTo(Class<? extends Model> targetModelClass) {
if (targetModelClass == null) {
throw new NullPointerException();
}
for (Association association : associations) {
if (association.getTargetClass().equals(targetModelClass)) {
return true;
}
}
return false;
}
public void removeAssociationForTarget(Class<? extends Model> modelClass) {
Association association = getAssociationForTarget(modelClass);
if (association != null) {
associations.remove(association);
}
}
}