/** * reads all methods by the action-annotations for building agent-actions * * @param p_class class * @param p_root root class * @return stream of all methods with inheritance */ private static Stream<Method> methods(final Class<?> p_class, final Class<?> p_root) { final Pair<Boolean, IAgentAction.EAccess> l_classannotation = CCommon.isActionClass(p_class); if (!l_classannotation.getLeft()) return p_class.getSuperclass() == null ? Stream.of() : methods(p_class.getSuperclass(), p_root); final Predicate<Method> l_filter = IAgentAction.EAccess.WHITELIST.equals(l_classannotation.getRight()) ? i -> !CCommon.isActionFiltered(i, p_root) : i -> CCommon.isActionFiltered(i, p_root); return Stream.concat( Arrays.stream(p_class.getDeclaredMethods()) .parallel() .map( i -> { i.setAccessible(true); return i; }) .filter(i -> !Modifier.isAbstract(i.getModifiers())) .filter(i -> !Modifier.isInterface(i.getModifiers())) .filter(i -> !Modifier.isNative(i.getModifiers())) .filter(i -> !Modifier.isStatic(i.getModifiers())) .filter(l_filter), methods(p_class.getSuperclass(), p_root)); }
/** * checks if an action is usable * * @param p_action action object * @return boolean usable flag */ private static boolean actionusable(final IAction p_action) { if ((p_action.name() == null) || (p_action.name().isEmpty()) || (p_action.name().get(0).trim().isEmpty())) { LOGGER.warning(CCommon.languagestring(CCommon.class, "actionnameempty")); return false; } if (!Character.isLetter(p_action.name().get(0).charAt(0))) { LOGGER.warning(CCommon.languagestring(CCommon.class, "actionletter", p_action)); return false; } if (!Character.isLowerCase(p_action.name().get(0).charAt(0))) { LOGGER.warning(CCommon.languagestring(CCommon.class, "actionlowercase", p_action)); return false; } if (p_action.minimalArgumentNumber() < 0) { LOGGER.warning(CCommon.languagestring(CCommon.class, "actionargumentsnumber", p_action)); return false; } return true; }
/** * returns actions by a class * * @note class must be an inheritance of the IAgent interface * @param p_class class list * @return action stream */ @SuppressWarnings("unchecked") public static Stream<IAction> actionsFromAgentClass(final Class<?>... p_class) { return p_class == null || p_class.length == 0 ? Stream.of() : Arrays.stream(p_class) .parallel() .filter(IAgent.class::isAssignableFrom) .flatMap(i -> CCommon.methods(i, i)) .map( i -> { try { return (IAction) new CMethodAction(i); } catch (final IllegalAccessException l_exception) { LOGGER.warning( CCommon.languagestring(CCommon.class, "actioninstantiate", i, l_exception)); return null; } }) // action can be instantiate .filter(Objects::nonNull) // check usable action name .filter(CCommon::actionusable); }
/** * creates a compression stream * * @param p_datastream data-counting stream * @return compression output stream * @throws IOException throws on any io error */ public final OutputStream get(final DataOutputStream p_datastream) throws IOException { switch (this) { case BZIP: return new BZip2CompressorOutputStream(p_datastream); case GZIP: return new GzipCompressorOutputStream(p_datastream); case DEFLATE: return new DeflateCompressorOutputStream(p_datastream); case PACK200: return new Pack200CompressorOutputStream(p_datastream); case XZ: return new XZCompressorOutputStream(p_datastream); default: throw new CIllegalStateException(CCommon.languagestring(this, "unknown", this)); } }
/** * get all classes within an Java package as action * * @param p_package full-qualified package name or empty for default package * @return action stream */ @SuppressWarnings("unchecked") public static Stream<IAction> actionsFromPackage(final String... p_package) { return ((p_package == null) || (p_package.length == 0) ? Stream.of(MessageFormat.format("{0}.{1}", PACKAGEROOT, "action.buildin")) : Arrays.stream(p_package)) .flatMap( j -> { try { return ClassPath.from(Thread.currentThread().getContextClassLoader()) .getTopLevelClassesRecursive(j) .parallelStream() .map(ClassPath.ClassInfo::load) .filter(i -> !Modifier.isAbstract(i.getModifiers())) .filter(i -> !Modifier.isInterface(i.getModifiers())) .filter(i -> Modifier.isPublic(i.getModifiers())) .filter(IAction.class::isAssignableFrom) .map( i -> { try { return (IAction) i.newInstance(); } catch (final IllegalAccessException | InstantiationException l_exception) { LOGGER.warning( CCommon.languagestring( CCommon.class, "actioninstantiate", i, l_exception)); return null; } }) // action can be instantiate .filter(Objects::nonNull) // check usable action name .filter(CCommon::actionusable); } catch (final IOException l_exception) { throw new UncheckedIOException(l_exception); } }); }
/** class for any helper calls */ public final class CCommon { /** package name */ public static final String PACKAGEROOT = "org.lightjason.agentspeak"; /** logger */ private static final Logger LOGGER = CCommon.logger(CCommon.class); /** language resource bundle */ private static final ResourceBundle LANGUAGE = ResourceBundle.getBundle( MessageFormat.format("{0}.{1}", PACKAGEROOT, "language"), Locale.getDefault(), new CUTF8Control()); /** properties of the package */ private static final ResourceBundle PROPERTIES = ResourceBundle.getBundle( MessageFormat.format("{0}.{1}", PACKAGEROOT, "configuration"), Locale.getDefault(), new CUTF8Control()); /** private ctor - avoid instantiation */ private CCommon() {} /** * returns a logger instance * * @param p_class class type * @return logger */ public static Logger logger(final Class<?> p_class) { return Logger.getLogger(p_class.getName()); } /** * list of usable languages * * @return list of language pattern */ public static String[] languages() { return Arrays.stream(PROPERTIES.getString("translation").split(",")) .map(i -> i.trim().toLowerCase()) .toArray(String[]::new); } /** * returns the language bundle * * @return bundle */ public static ResourceBundle languagebundle() { return LANGUAGE; } /** * returns the property data of the package * * @return bundle object */ public static ResourceBundle configuration() { return PROPERTIES; } // --------------------------------------------------------------------------------------------------------------------------------------------------------- /** * get all classes within an Java package as action * * @param p_package full-qualified package name or empty for default package * @return action stream */ @SuppressWarnings("unchecked") public static Stream<IAction> actionsFromPackage(final String... p_package) { return ((p_package == null) || (p_package.length == 0) ? Stream.of(MessageFormat.format("{0}.{1}", PACKAGEROOT, "action.buildin")) : Arrays.stream(p_package)) .flatMap( j -> { try { return ClassPath.from(Thread.currentThread().getContextClassLoader()) .getTopLevelClassesRecursive(j) .parallelStream() .map(ClassPath.ClassInfo::load) .filter(i -> !Modifier.isAbstract(i.getModifiers())) .filter(i -> !Modifier.isInterface(i.getModifiers())) .filter(i -> Modifier.isPublic(i.getModifiers())) .filter(IAction.class::isAssignableFrom) .map( i -> { try { return (IAction) i.newInstance(); } catch (final IllegalAccessException | InstantiationException l_exception) { LOGGER.warning( CCommon.languagestring( CCommon.class, "actioninstantiate", i, l_exception)); return null; } }) // action can be instantiate .filter(Objects::nonNull) // check usable action name .filter(CCommon::actionusable); } catch (final IOException l_exception) { throw new UncheckedIOException(l_exception); } }); } /** * returns actions by a class * * @note class must be an inheritance of the IAgent interface * @param p_class class list * @return action stream */ @SuppressWarnings("unchecked") public static Stream<IAction> actionsFromAgentClass(final Class<?>... p_class) { return p_class == null || p_class.length == 0 ? Stream.of() : Arrays.stream(p_class) .parallel() .filter(IAgent.class::isAssignableFrom) .flatMap(i -> CCommon.methods(i, i)) .map( i -> { try { return (IAction) new CMethodAction(i); } catch (final IllegalAccessException l_exception) { LOGGER.warning( CCommon.languagestring(CCommon.class, "actioninstantiate", i, l_exception)); return null; } }) // action can be instantiate .filter(Objects::nonNull) // check usable action name .filter(CCommon::actionusable); } /** * checks if an action is usable * * @param p_action action object * @return boolean usable flag */ private static boolean actionusable(final IAction p_action) { if ((p_action.name() == null) || (p_action.name().isEmpty()) || (p_action.name().get(0).trim().isEmpty())) { LOGGER.warning(CCommon.languagestring(CCommon.class, "actionnameempty")); return false; } if (!Character.isLetter(p_action.name().get(0).charAt(0))) { LOGGER.warning(CCommon.languagestring(CCommon.class, "actionletter", p_action)); return false; } if (!Character.isLowerCase(p_action.name().get(0).charAt(0))) { LOGGER.warning(CCommon.languagestring(CCommon.class, "actionlowercase", p_action)); return false; } if (p_action.minimalArgumentNumber() < 0) { LOGGER.warning(CCommon.languagestring(CCommon.class, "actionargumentsnumber", p_action)); return false; } return true; } /** * reads all methods by the action-annotations for building agent-actions * * @param p_class class * @param p_root root class * @return stream of all methods with inheritance */ private static Stream<Method> methods(final Class<?> p_class, final Class<?> p_root) { final Pair<Boolean, IAgentAction.EAccess> l_classannotation = CCommon.isActionClass(p_class); if (!l_classannotation.getLeft()) return p_class.getSuperclass() == null ? Stream.of() : methods(p_class.getSuperclass(), p_root); final Predicate<Method> l_filter = IAgentAction.EAccess.WHITELIST.equals(l_classannotation.getRight()) ? i -> !CCommon.isActionFiltered(i, p_root) : i -> CCommon.isActionFiltered(i, p_root); return Stream.concat( Arrays.stream(p_class.getDeclaredMethods()) .parallel() .map( i -> { i.setAccessible(true); return i; }) .filter(i -> !Modifier.isAbstract(i.getModifiers())) .filter(i -> !Modifier.isInterface(i.getModifiers())) .filter(i -> !Modifier.isNative(i.getModifiers())) .filter(i -> !Modifier.isStatic(i.getModifiers())) .filter(l_filter), methods(p_class.getSuperclass(), p_root)); } /** * filter of a class to use it as action * * @param p_class class for checking * @return boolean flag of check result */ private static Pair<Boolean, IAgentAction.EAccess> isActionClass(final Class<?> p_class) { if (!p_class.isAnnotationPresent(IAgentAction.class)) return new ImmutablePair<>(false, IAgentAction.EAccess.BLACKLIST); final IAgentAction l_annotation = p_class.getAnnotation(IAgentAction.class); return new ImmutablePair<>( (l_annotation.classes().length == 0) || (Arrays.stream(p_class.getAnnotation(IAgentAction.class).classes()) .parallel() .anyMatch(p_class::equals)), l_annotation.access()); } /** * class filter of an action to use it * * @param p_method method for checking * @param p_root root class * @return boolean flag of check result */ private static boolean isActionFiltered(final Method p_method, final Class<?> p_root) { return p_method.isAnnotationPresent(IAgentActionFilter.class) && ((p_method.getAnnotation(IAgentActionFilter.class).classes().length == 0) || (Arrays.stream(p_method.getAnnotation(IAgentActionFilter.class).classes()) .parallel() .anyMatch(p_root::equals))); } // --------------------------------------------------------------------------------------------------------------------------------------------------------- /** * concats an URL with a path * * @param p_base base URL * @param p_string additional path * @return new URL * @throws URISyntaxException thrown on syntax error * @throws MalformedURLException thrown on malformat */ public static URL concaturl(final URL p_base, final String p_string) throws MalformedURLException, URISyntaxException { return new URL(p_base.toString() + p_string).toURI().normalize().toURL(); } /** * returns root path of the resource * * @return URL of file or null */ public static URL resourceurl() { return CCommon.class.getClassLoader().getResource(""); } /** * returns a file from a resource e.g. Jar file * * @param p_file file * @return URL of file or null * @throws URISyntaxException thrown on syntax error * @throws MalformedURLException thrown on malformat */ public static URL resourceurl(final String p_file) throws URISyntaxException, MalformedURLException { return resourceurl(new File(p_file)); } /** * returns a file from a resource e.g. Jar file * * @param p_file file relative to the CMain * @return URL of file or null * @throws URISyntaxException is thrown on URI errors * @throws MalformedURLException is thrown on malformat */ private static URL resourceurl(final File p_file) throws URISyntaxException, MalformedURLException { if (p_file.exists()) return p_file.toURI().normalize().toURL(); return CCommon.class .getClassLoader() .getResource(p_file.toString().replace(File.separator, "/")) .toURI() .normalize() .toURL(); } // --------------------------------------------------------------------------------------------------------------------------------------------------------- /** * returns the language depend string on any object * * @param p_source any object * @param p_label label name * @param p_parameter parameter * @return translated string * @tparam T object type */ public static <T> String languagestring( final T p_source, final String p_label, final Object... p_parameter) { return languagestring(p_source.getClass(), p_label, p_parameter); } /** * returns a string of the resource file * * @param p_class class for static calls * @param p_label label name of the object * @param p_parameter object array with substitutions * @return resource string */ public static String languagestring( final Class<?> p_class, final String p_label, final Object... p_parameter) { try { return MessageFormat.format(LANGUAGE.getString(languagelabel(p_class, p_label)), p_parameter); } catch (final MissingResourceException l_exception) { return ""; } } /** * returns the label of a class and string to get access to the resource * * @param p_class class for static calls * @param p_label label name of the object * @return label name */ private static String languagelabel(final Class<?> p_class, final String p_label) { return (p_class.getCanonicalName().toLowerCase(Locale.ROOT) + "." + p_label.toLowerCase(Locale.ROOT)) .replaceAll("[^a-zA-Z0-9_\\.]+", "") .replace(PACKAGEROOT + ".", ""); } // --------------------------------------------------------------------------------------------------------------------------------------------------------- /** * class to read UTF-8 encoded property file * * @note Java default encoding for property files is ISO-Latin-1 */ private static final class CUTF8Control extends ResourceBundle.Control { public final ResourceBundle newBundle( final String p_basename, final Locale p_locale, final String p_format, final ClassLoader p_loader, final boolean p_reload) throws IllegalAccessException, InstantiationException, IOException { final InputStream l_stream; final String l_resource = this.toResourceName(this.toBundleName(p_basename, p_locale), "properties"); if (!p_reload) l_stream = p_loader.getResourceAsStream(l_resource); else { final URL l_url = p_loader.getResource(l_resource); if (l_url == null) return null; final URLConnection l_connection = l_url.openConnection(); if (l_connection == null) return null; l_connection.setUseCaches(false); l_stream = l_connection.getInputStream(); } final ResourceBundle l_bundle = new PropertyResourceBundle(new InputStreamReader(l_stream, "UTF-8")); l_stream.close(); return l_bundle; } } }