Java tutorial
/* * @cond LICENSE * ###################################################################################### * # LGPL License # * # # * # This file is part of the LightJason AgentSpeak(L++) # * # Copyright (c) 2015-16, LightJason (info@lightjason.org) # * # This program is free software: you can redistribute it and/or modify # * # it under the terms of the GNU Lesser General Public License as # * # published by the Free Software Foundation, either version 3 of the # * # License, or (at your option) any later version. # * # # * # This program 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 this program. If not, see http://www.gnu.org/licenses/ # * ###################################################################################### * @endcond */ package org.lightjason.agentspeak.language; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; import org.apache.commons.compress.compressors.deflate.DeflateCompressorOutputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; import org.apache.commons.compress.compressors.pack200.Pack200CompressorOutputStream; import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.io.output.NullOutputStream; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.lightjason.agentspeak.agent.IAgent; import org.lightjason.agentspeak.error.CIllegalArgumentException; import org.lightjason.agentspeak.error.CIllegalStateException; import org.lightjason.agentspeak.language.execution.CContext; import org.lightjason.agentspeak.language.execution.IContext; import org.lightjason.agentspeak.language.execution.action.unify.IUnifier; import org.lightjason.agentspeak.language.instantiable.IInstantiable; import org.lightjason.agentspeak.language.instantiable.plan.IPlan; import org.lightjason.agentspeak.language.instantiable.plan.trigger.ITrigger; import org.lightjason.agentspeak.language.variable.CConstant; import org.lightjason.agentspeak.language.variable.IVariable; import java.io.ByteArrayInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; /** * common structure for execution definition */ public final class CCommon { /** * private ctor - avoid instantiation */ private CCommon() { } //--- plan / rule instantiation ---------------------------------------------------------------------------------------------------------------------------- /** * updates within an instance context all variables of the stream * * @param p_context context * @param p_unifiedvariables unified variables as stream * @return context reference */ public static IContext updatecontext(final IContext p_context, final Stream<IVariable<?>> p_unifiedvariables) { p_unifiedvariables.forEach(i -> p_context.instancevariables().get(i.fqnfunctor()).set(i.raw())); return p_context; } /** * creates the instantiate execution context with default variables * * @param p_instance instance object * @param p_agent agent * @param p_variable variable stream * @return context object */ public static IContext instantiate(final IInstantiable p_instance, final IAgent<?> p_agent, final Stream<IVariable<?>> p_variable) { final Set<IVariable<?>> l_variables = p_instance.variables().parallel().map(i -> i.shallowcopy()) .collect(Collectors.toSet()); Stream.of(p_variable, p_agent.variablebuilder().generate(p_agent, p_instance), Stream.of(new CConstant<>("Score", p_instance.score(p_agent))), Stream.of(new CConstant<>("Cycle", p_agent.cycle()))).reduce(Stream::concat) .orElseGet(Stream::<IVariable<?>>empty).forEach(i -> { l_variables.remove(i); l_variables.add(i); }); return new CContext(p_agent, p_instance, Collections.unmodifiableSet(l_variables)); } /** * unifies trigger and creates the set of variables * * @note target trigger literal must be cloned to avoid variable overwriting * @param p_unifier unifier * @param p_source input trigger (with values) * @param p_target trigger (of a plan / rule) * @return pair of valid unification and unified variables */ public static Pair<Boolean, Set<IVariable<?>>> unifytrigger(final IUnifier p_unifier, final ITrigger p_source, final ITrigger p_target) { // filter for avoid duplicated instantiation on non-existing values / annotations if (!((p_source.getLiteral().emptyValues() == p_target.getLiteral().emptyValues()) && (p_source.getLiteral().emptyAnnotations() == p_target.getLiteral().emptyAnnotations()))) return new ImmutablePair<>(false, Collections.emptySet()); // unify variables, source trigger literal must be copied final Set<IVariable<?>> l_variables = p_unifier.literal(p_target.getLiteral().deepcopy().<ILiteral>raw(), p_source.getLiteral()); // check for completely unification (of all variables) return l_variables.size() == CCommon.variablefrequency(p_target.getLiteral()).size() ? new ImmutablePair<>(true, l_variables) : new ImmutablePair<>(false, Collections.emptySet()); } /** * instantiate a plan with context and plan-specific variables * * @param p_plan plan * @param p_fail fail runs * @param p_success successful runs * @param p_agent agent * @param p_variables instantiated variables * @return pair of plan and context object */ public static Pair<IPlan, IContext> instantiateplan(final IPlan p_plan, final double p_fail, final double p_success, final IAgent<?> p_agent, final Set<IVariable<?>> p_variables) { final double l_sum = p_success + p_fail; return new ImmutablePair<>(p_plan, p_plan.instantiate(p_agent, Stream.concat(p_variables.stream(), Stream.of( // execution count new CConstant<>("PlanSuccessful", p_success), new CConstant<>("PlanFail", p_fail), new CConstant<>("PlanRuns", l_sum), // execution ratio new CConstant<>("PlanSuccessfulRatio", l_sum == 0 ? 0 : p_success / l_sum), new CConstant<>("PlanFailRatio", l_sum == 0 ? 0 : p_fail / l_sum))))); } // --- variable / term helpers ----------------------------------------------------------------------------------------------------------------------------- /** * consts the variables within a literal * * @param p_literal literal * @return map with frequency */ public static Map<IVariable<?>, Integer> variablefrequency(final ILiteral p_literal) { return Collections.unmodifiableMap( Stream.concat(recursiveterm(p_literal.orderedvalues()), recursiveliteral(p_literal.annotations())) .filter(i -> i instanceof IVariable<?>).map(i -> (IVariable<?>) i) .collect(Collectors.toMap(i -> i, i -> 1, Integer::sum))); } /** * checks a term value for assignable class * * @param p_value any value type * @param p_class assignable class * @return term value or raw value */ @SuppressWarnings("unchecked") public static <T> boolean rawvalueAssignableTo(final T p_value, final Class<?>... p_class) { if (p_value instanceof IVariable<?>) return ((IVariable<?>) p_value).valueAssignableTo(p_class); if (p_value instanceof IRawTerm<?>) return ((IRawTerm<?>) p_value).valueAssignableTo(p_class); return Arrays.stream(p_class).anyMatch(i -> i.isAssignableFrom(p_value.getClass())); } /** * replace variables with context variables * * @param p_context execution context * @param p_terms replacing term list * @return result term list */ public static List<ITerm> replaceFromContext(final IContext p_context, final Collection<? extends ITerm> p_terms) { return p_terms.stream().map(i -> replaceFromContext(p_context, i)).collect(Collectors.toList()); } /** * replace variable with context variable * other values will be passed without context access * * @param p_context execution context * @param p_term term * @return replaces variable object */ public static ITerm replaceFromContext(final IContext p_context, final ITerm p_term) { if (!(p_term instanceof IVariable<?>)) return p_term; final IVariable<?> l_variable = p_context.instancevariables().get(p_term.fqnfunctor()); if (l_variable != null) return l_variable; throw new CIllegalArgumentException(org.lightjason.agentspeak.common.CCommon.languagestring(CCommon.class, "variablenotfoundincontext", p_term.fqnfunctor())); } /** * flat term-in-term collection into * a straight term list * * @param p_terms term collection * @return flat term stream */ public static Stream<ITerm> flatcollection(final Collection<? extends ITerm> p_terms) { return flattenToStream(p_terms); } /** * flats and concat the term list * * @param p_input input term list * @return byte sequence with UTF-8 encoding * * @throws UnsupportedEncodingException is thrown on wrong encoding type */ public static byte[] getBytes(final List<ITerm> p_input) throws UnsupportedEncodingException { final StringBuilder l_result = new StringBuilder(); (flatcollection(p_input)).forEach(i -> l_result.append(i.raw().toString())); return l_result.toString().getBytes("UTF-8"); } /** * recursive stream of term values * * @param p_input term stream * @return term stream */ public static Stream<ITerm> recursiveterm(final Stream<ITerm> p_input) { return p_input.flatMap( i -> i instanceof ILiteral ? recursiveterm((i.<ILiteral>raw()).orderedvalues()) : Stream.of(i)); } /** * recursive stream of literal values * * @param p_input term stream * @return term stream * * @note annotations cannot use annotation within */ public static Stream<ITerm> recursiveliteral(final Stream<ILiteral> p_input) { return p_input.flatMap(i -> recursiveterm(i.orderedvalues())); } /* * recursive flattering of a list structure * * @param p_list any collection type * @return term stream */ @SuppressWarnings("unchecked") private static Stream<ITerm> flattenToStream(final Collection<?> p_list) { return p_list.stream().flatMap(i -> { final Object l_value = i instanceof ITerm ? ((ITerm) i).raw() : i; return l_value instanceof Collection<?> ? flattenToStream((Collection<?>) l_value) : Stream.of(CRawTerm.from(l_value)); }); } /** * returns the hasing function for term data * * @return hasher */ public static Hasher getTermHashing() { return Hashing.murmur3_32().newHasher(); } // --- compression algorithm ------------------------------------------------------------------------------------------------------------------------------- /** * calculates the levenshtein distance * * @param p_first first string * @param p_second second string * @param p_insertweight inserting weight * @param p_replaceweight replace weight * @param p_deleteweight delete weight * @return distance * @see https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#Java */ public static double levenshtein(final String p_first, final String p_second, final double p_insertweight, final double p_replaceweight, final double p_deleteweight) { // the array of distances double[] l_cost = IntStream.range(0, p_first.length() + 1).mapToDouble(i -> i).toArray(); double[] l_newcost = new double[l_cost.length]; for (int j = 1; j < p_second.length() + 1; j++) { l_newcost[0] = j; // calculate cost of operation for all characters for (int i = 1; i < l_cost.length; i++) l_newcost[i] = min( l_cost[i - 1] + (p_first.charAt(i - 1) == p_second.charAt(j - 1) ? 0 : p_replaceweight), l_newcost[i - 1] + p_deleteweight, l_cost[i] + p_insertweight); final double[] l_swap = l_cost; l_cost = l_newcost; l_newcost = l_swap; } return l_cost[p_first.length()]; } /** * returns the minimum of three elemens * * @param p_first first value * @param p_second second value * @param p_third third value * @return minimum */ public static double min(final double p_first, final double p_second, final double p_third) { return Math.min(Math.min(p_first, p_second), p_third); } /** * normalized-compression-distance * * @param p_compression compression algorithm * @param p_first first string * @param p_second second string * @return distance in [0,1] */ public static double ncd(final ECompression p_compression, final String p_first, final String p_second) { final double l_first = compress(p_compression, p_first); final double l_second = compress(p_compression, p_second); return (compress(p_compression, p_first + p_second) - Math.min(l_first, l_second)) / Math.max(l_first, l_second); } /** * compression algorithm * * @param p_compression compression algorithm * @param p_input input string * @return number of compression bytes * @warning counting stream returns the correct number of bytes after flushing */ private static double compress(final ECompression p_compression, final String p_input) { final DataOutputStream l_counting = new DataOutputStream(new NullOutputStream()); try (final InputStream l_input = new ByteArrayInputStream(p_input.getBytes(StandardCharsets.UTF_8)); final OutputStream l_compress = p_compression.get(l_counting)) { IOUtils.copy(l_input, l_compress); } catch (final IOException l_exception) { return 0; } return l_counting.size(); } /** * compression algorithm */ public enum ECompression { BZIP, GZIP, DEFLATE, PACK200, XZ; /** * enum names */ private static final Set<String> ALGORITHMS = Collections .unmodifiableSet(Arrays.stream(ECompression.values()).map(i -> i.name().toUpperCase(Locale.ROOT)) .collect(Collectors.toSet())); /** * 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( org.lightjason.agentspeak.common.CCommon.languagestring(this, "unknown", this)); } } /** * returns a compression value * * @param p_value string name * @return compression value */ public static ECompression from(final String p_value) { return ECompression.valueOf(p_value.toUpperCase(Locale.ROOT)); } /** * checks if a compression exists * * @param p_value compression name * @return existance flag */ public static boolean exist(final String p_value) { return ALGORITHMS.contains(p_value.toUpperCase(Locale.ROOT)); } } }