diff --git a/lphy-base/src/main/java/lphy/base/spi/LPhyBaseImpl.java b/lphy-base/src/main/java/lphy/base/spi/LPhyBaseImpl.java index 80525df23..edbeaf2e5 100644 --- a/lphy-base/src/main/java/lphy/base/spi/LPhyBaseImpl.java +++ b/lphy-base/src/main/java/lphy/base/spi/LPhyBaseImpl.java @@ -35,7 +35,7 @@ import lphy.base.function.tree.PruneTree; import lphy.core.model.BasicFunction; import lphy.core.model.GenerativeDistribution; -import lphy.core.spi.LPhyExtension; +import lphy.core.spi.LPhyCoreImpl; import java.util.Arrays; import java.util.List; @@ -46,67 +46,73 @@ * It requires a public no-args constructor. * @author Walter Xie */ -public class LPhyBaseImpl implements LPhyExtension { +public class LPhyBaseImpl extends LPhyCoreImpl { //implements LPhyExtension { static { SequenceTypeLoader sequenceTypeLoader = new SequenceTypeLoader(); sequenceTypeLoader.loadAllExtensions(); } - List> genDists = Arrays.asList( - // probability distribution - Bernoulli.class, BernoulliMulti.class, Beta.class, Categorical.class, Cauchy.class, Dirichlet.class, - DiscretizedGamma.class, Exp.class, Gamma.class, Geometric.class, InverseGamma.class, LogNormal.class, - NegativeBinomial.class, Normal.class, NormalGamma.class, Poisson.class, - Uniform.class, UniformDiscrete.class, Weibull.class, WeightedDirichlet.class, - // tree distribution - Yule.class, BirthDeathTree.class, FullBirthDeathTree.class, BirthDeathTreeDT.class, - BirthDeathSamplingTree.class, BirthDeathSamplingTreeDT.class, BirthDeathSerialSamplingTree.class, - RhoSampleTree.class, FossilBirthDeathTree.class, - SimBDReverse.class, SimFBDAge.class, SimFossilsPoisson.class, - SerialCoalescent.class, StructuredCoalescent.class, MultispeciesCoalescent.class, - // skyline - SkylineCoalescent.class, ExpMarkovChain.class, RandomComposition.class, - // alignment - Sequence.class, ErrorModel.class, MissingSites.class, - // others - RandomBooleanArray.class, Sample.class, - // phylogenetic distribution - PhyloBrownian.class, PhyloMultivariateBrownian.class, PhyloOU.class, - PhyloCTMC.class, PhyloCTMCSiteModel.class, bSiteRates.class); + @Override + public List> declareDistributions() { + return Arrays.asList( + // probability distribution + Bernoulli.class, BernoulliMulti.class, Beta.class, Categorical.class, Cauchy.class, Dirichlet.class, + DiscretizedGamma.class, Exp.class, Gamma.class, Geometric.class, InverseGamma.class, LogNormal.class, + NegativeBinomial.class, Normal.class, NormalGamma.class, Poisson.class, + Uniform.class, UniformDiscrete.class, Weibull.class, WeightedDirichlet.class, + // tree distribution + Yule.class, BirthDeathTree.class, FullBirthDeathTree.class, BirthDeathTreeDT.class, + BirthDeathSamplingTree.class, BirthDeathSamplingTreeDT.class, BirthDeathSerialSamplingTree.class, + RhoSampleTree.class, FossilBirthDeathTree.class, + SimBDReverse.class, SimFBDAge.class, SimFossilsPoisson.class, + SerialCoalescent.class, StructuredCoalescent.class, MultispeciesCoalescent.class, + // skyline + SkylineCoalescent.class, ExpMarkovChain.class, RandomComposition.class, + // alignment + Sequence.class, ErrorModel.class, MissingSites.class, + // others + RandomBooleanArray.class, Sample.class, + // phylogenetic distribution + PhyloBrownian.class, PhyloMultivariateBrownian.class, PhyloOU.class, + PhyloCTMC.class, PhyloCTMCSiteModel.class, bSiteRates.class); + } - List> functions = Arrays.asList(ARange.class, ArgI.class, - // Substitution models - JukesCantor.class, K80.class, F81.class, HKY.class, GTR.class, WAG.class, - GeneralTimeReversible.class, LewisMK.class, - NucleotideModel.class, - BModelSetFunction.class, - bSiteModelFunction.class, + @Override + public List> declareFunctions() { + return Arrays.asList(ARange.class, ArgI.class, + // Substitution models + JukesCantor.class, K80.class, F81.class, HKY.class, GTR.class, WAG.class, + GeneralTimeReversible.class, LewisMK.class, + NucleotideModel.class, + BModelSetFunction.class, + bSiteModelFunction.class, - // Data types - BinaryDatatypeFunction.class, NucleotidesFunction.class, StandardDatatypeFunction.class, - AminoAcidsFunction.class, - // Taxa - CreateTaxa.class, ExtantTaxa.class, NCharFunction.class, NTaxaFunction.class, TaxaFunction.class, - // Alignment - SelectSitesByMissingFraction.class, RemoveTaxa.class, - VariableSites.class, InvariableSites.class, CopySites.class, - // Tree - LocalBranchRates.class, ExtantTree.class, PruneTree.class, //NodeCount.class, TreeLength.class, - // Matrix - BinaryRateMatrix.class, MigrationMatrix.class, MigrationCount.class, - // IO - Newick.class, ReadNexus.class, ReadFasta.class, ReadDelim.class, ExtractTrait.class, SpeciesTaxa.class, - // Math - SumBoolean.class, SumRows.class, SumCols.class, Sum2dArray.class, Sum.class,// Product.class, - // Set Op - Intersect.class, - // cast - ToDouble.class, - // Utils - Length.class, Unique.class, Sort.class, IfElse.class, //ConcatStr.class, - Get.class, Select.class, Split.class, ParseInt.class, Rep.class, RepArray.class, //Copy.class, - ConcatArray.class, Concat2Str.class); + // Data types + BinaryDatatypeFunction.class, NucleotidesFunction.class, StandardDatatypeFunction.class, + AminoAcidsFunction.class, + // Taxa + CreateTaxa.class, ExtantTaxa.class, NCharFunction.class, NTaxaFunction.class, TaxaFunction.class, + // Alignment + SelectSitesByMissingFraction.class, RemoveTaxa.class, + VariableSites.class, InvariableSites.class, CopySites.class, + // Tree + LocalBranchRates.class, ExtantTree.class, PruneTree.class, //NodeCount.class, TreeLength.class, + // Matrix + BinaryRateMatrix.class, MigrationMatrix.class, MigrationCount.class, + // IO + Newick.class, ReadNexus.class, ReadFasta.class, ReadDelim.class, ExtractTrait.class, SpeciesTaxa.class, + // Math + SumBoolean.class, SumRows.class, SumCols.class, Sum2dArray.class, Sum.class,// Product.class, + // Set Op + Intersect.class, + // cast + ToDouble.class, + // Utils + Length.class, Unique.class, Sort.class, IfElse.class, //ConcatStr.class, + Get.class, Select.class, Split.class, ParseInt.class, Rep.class, RepArray.class, //Copy.class, + ConcatArray.class, Concat2Str.class); + } /** * Required by ServiceLoader. @@ -115,15 +121,26 @@ public LPhyBaseImpl() { //TODO do something here, e.g. print package or classes info ? } - @Override - public List> getDistributions() { - return genDists; - } - @Override - public List> getFunctions() { - return functions; - } +// @Override +// public void register() { +// +// } +// +// @Override +// public Map>> getDistributions() { +// return null; +// } +// +// @Override +// public Map>> getFunctions() { +// return null; +// } +// +// @Override +// public TreeSet> getTypes() { +// return null; +// } public String getExtensionName() { return "LPhy standard library"; diff --git a/lphy-base/src/main/java/lphy/base/spi/LPhyBaseValueFormatterImpl.java b/lphy-base/src/main/java/lphy/base/spi/LPhyValueFormatterBaseImpl.java similarity index 56% rename from lphy-base/src/main/java/lphy/base/spi/LPhyBaseValueFormatterImpl.java rename to lphy-base/src/main/java/lphy/base/spi/LPhyValueFormatterBaseImpl.java index bafce27d8..e12e92145 100644 --- a/lphy-base/src/main/java/lphy/base/spi/LPhyBaseValueFormatterImpl.java +++ b/lphy-base/src/main/java/lphy/base/spi/LPhyValueFormatterBaseImpl.java @@ -3,7 +3,7 @@ import lphy.base.logger.NexusAlignmentFormatter; import lphy.base.logger.NexusTreeFormatter; import lphy.core.logger.ValueFormatter; -import lphy.core.spi.LPhyValueFormatter; +import lphy.core.spi.LPhyValueFormatterCoreImpl; import java.util.Set; @@ -13,23 +13,19 @@ * It requires a public no-args constructor. * @author Walter Xie */ -public class LPhyBaseValueFormatterImpl implements LPhyValueFormatter { +public class LPhyValueFormatterBaseImpl extends LPhyValueFormatterCoreImpl {//implements LPhyValueFormatter { - /** - * Required by ServiceLoader. - */ - public LPhyBaseValueFormatterImpl() { - } @Override - public Set> getValueFormatters() { + public Set> declareValueFormatters() { return Set.of(NexusAlignmentFormatter.class, NexusTreeFormatter.class); } -// public Map, Set>> getValueFormatterMap() { -// return Map.of( SimpleAlignment.class, Set.of(NexusAlignmentFormatter.class), -// TimeTree.class, Set.of(NexusTreeFormatter.class) ); -// } + /** + * Required by ServiceLoader. + */ + public LPhyValueFormatterBaseImpl() { + } public String getExtensionName() { return "LPhy base loggers"; diff --git a/lphy-base/src/main/java/lphy/base/spi/SequenceTypeBaseImpl.java b/lphy-base/src/main/java/lphy/base/spi/SequenceTypeBaseImpl.java index c9d74ecb8..fa01a817e 100644 --- a/lphy-base/src/main/java/lphy/base/spi/SequenceTypeBaseImpl.java +++ b/lphy-base/src/main/java/lphy/base/spi/SequenceTypeBaseImpl.java @@ -23,7 +23,7 @@ public SequenceTypeBaseImpl() { } @Override - public Map getSequenceTypes() { + public Map declareSequenceTypes() { Map dataTypeMap = new ConcurrentHashMap<>(); dataTypeMap.put("rna", SequenceType.NUCLEOTIDE); dataTypeMap.put("dna", SequenceType.NUCLEOTIDE); @@ -37,6 +37,11 @@ public SequenceTypeBaseImpl() { return dataTypeMap; } + @Override + public void register() { + + } + public String getExtensionName() { return "LPhy base"; } diff --git a/lphy-base/src/main/java/lphy/base/spi/SequenceTypeExtension.java b/lphy-base/src/main/java/lphy/base/spi/SequenceTypeExtension.java index ecbfe2ea5..44990d59f 100644 --- a/lphy-base/src/main/java/lphy/base/spi/SequenceTypeExtension.java +++ b/lphy-base/src/main/java/lphy/base/spi/SequenceTypeExtension.java @@ -20,6 +20,6 @@ public interface SequenceTypeExtension extends Extension { * The string key is a keyword to represent this SequenceType. * The keyword can be used to identify and initialise the corresponding sequence type. */ - Map getSequenceTypes(); + Map declareSequenceTypes(); } diff --git a/lphy-base/src/main/java/lphy/base/spi/SequenceTypeLoader.java b/lphy-base/src/main/java/lphy/base/spi/SequenceTypeLoader.java index e9cbe97f8..723d34140 100644 --- a/lphy-base/src/main/java/lphy/base/spi/SequenceTypeLoader.java +++ b/lphy-base/src/main/java/lphy/base/spi/SequenceTypeLoader.java @@ -52,7 +52,7 @@ private void registerExtensions(String extClsName) { System.out.println("Registering extension from " + sequenceTypeExtension.getClass().getName()); // sequence types - Map newDataTypes = sequenceTypeExtension.getSequenceTypes(); + Map newDataTypes = sequenceTypeExtension.declareSequenceTypes(); if (newDataTypes != null) // TODO validate same sequence type? newDataTypes.forEach(dataTypeMap::putIfAbsent); diff --git a/lphy-base/src/main/java/module-info.java b/lphy-base/src/main/java/module-info.java index d6dbb949a..6cddccf49 100644 --- a/lphy-base/src/main/java/module-info.java +++ b/lphy-base/src/main/java/module-info.java @@ -1,5 +1,5 @@ import lphy.base.spi.LPhyBaseImpl; -import lphy.base.spi.LPhyBaseValueFormatterImpl; +import lphy.base.spi.LPhyValueFormatterBaseImpl; import lphy.base.spi.SequenceTypeBaseImpl; /** @@ -52,12 +52,9 @@ exports lphy.base.logger; // LPhy extensions - uses lphy.core.spi.LPhyExtension; + uses lphy.core.spi.Extension; // declare what service interface the provider intends to use - provides lphy.core.spi.LPhyExtension with LPhyBaseImpl; - - uses lphy.core.spi.LPhyValueFormatter; - provides lphy.core.spi.LPhyValueFormatter with LPhyBaseValueFormatterImpl; + provides lphy.core.spi.Extension with LPhyBaseImpl, LPhyValueFormatterBaseImpl; uses lphy.base.spi.SequenceTypeExtension; // declare what service interface the provider intends to use diff --git a/lphy-studio/src/main/java/lphystudio/app/docgenerator/GenerateDocs.java b/lphy-studio/src/main/java/lphystudio/app/docgenerator/GenerateDocs.java index cb898c3e8..60271b421 100644 --- a/lphy-studio/src/main/java/lphystudio/app/docgenerator/GenerateDocs.java +++ b/lphy-studio/src/main/java/lphystudio/app/docgenerator/GenerateDocs.java @@ -66,7 +66,7 @@ public static void main(String[] args) throws IOException { // class name with package that implements {@link LPhyExtension} String clsName = args[2]; // require init ParserLoader - LoaderManager.getLphyCoreLoader().loadExtension(clsName); + LoaderManager.getLphyCoreLoader().loadExtensions(); //TODO broken } System.out.println("Creating doc for " + extName + " ...\n"); diff --git a/lphy-studio/src/main/java/lphystudio/app/manager/ExtManager.java b/lphy-studio/src/main/java/lphystudio/app/manager/ExtManager.java index 9f5ce5e08..b2c25221d 100644 --- a/lphy-studio/src/main/java/lphystudio/app/manager/ExtManager.java +++ b/lphy-studio/src/main/java/lphystudio/app/manager/ExtManager.java @@ -1,7 +1,6 @@ package lphystudio.app.manager; import lphy.core.spi.LPhyCoreLoader; -import lphy.core.spi.LPhyExtension; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -24,15 +23,15 @@ public class ExtManager { // pin lphy on the top of the list public final static String LPHY_ID = "lphy"; - final List extensions; + final List extensions; Set jarDirSet = new HashSet<>(); public ExtManager() { //TODO other loaders ? LPhyCoreLoader lphyCoreLoader = new LPhyCoreLoader(); - lphyCoreLoader.loadAllExtensions(); + lphyCoreLoader.loadExtensions(); - extensions = lphyCoreLoader.getExtensions(); + extensions = lphyCoreLoader.getExtensionMap().values().stream().toList(); System.out.println(extensions); } @@ -44,7 +43,7 @@ public List getLoadedLPhyExts() { List extList = new ArrayList<>(); // find all loaded lphy exts - for (LPhyExtension ext : extensions) { + for (lphy.core.spi.Extension ext : extensions) { Class cls = ext.getClass(); // String pkgNm = "/" + cls.getPackageName().replace(".", "/"); // include jar name diff --git a/lphy-studio/src/main/java/lphystudio/spi/LPhyStudioImpl.java b/lphy-studio/src/main/java/lphystudio/spi/LPhyStudioImpl.java index 08185013a..5f4e9e718 100644 --- a/lphy-studio/src/main/java/lphystudio/spi/LPhyStudioImpl.java +++ b/lphy-studio/src/main/java/lphystudio/spi/LPhyStudioImpl.java @@ -2,7 +2,7 @@ import lphy.core.model.BasicFunction; import lphy.core.model.GenerativeDistribution; -import lphy.core.spi.LPhyExtension; +import lphy.core.spi.LPhyCoreImpl; import java.util.ArrayList; import java.util.List; @@ -11,7 +11,7 @@ * Empty class to show studio ext in the Extension Manager. * @author Walter Xie */ -public class LPhyStudioImpl implements LPhyExtension { +public class LPhyStudioImpl extends LPhyCoreImpl { //} implements LPhyExtension { /** * Required by ServiceLoader. @@ -20,15 +20,17 @@ public LPhyStudioImpl() { } @Override - public List> getDistributions() { + public List> declareDistributions() { return new ArrayList<>(); } @Override - public List> getFunctions() { + public List> declareFunctions() { return new ArrayList<>(); } + + public String getExtensionName() { return "LPhy studio"; } diff --git a/lphy-studio/src/main/java/lphystudio/spi/LPhyStudioValueFormatterImpl.java b/lphy-studio/src/main/java/lphystudio/spi/LPhyValueFormatterStudioImpl.java similarity index 69% rename from lphy-studio/src/main/java/lphystudio/spi/LPhyStudioValueFormatterImpl.java rename to lphy-studio/src/main/java/lphystudio/spi/LPhyValueFormatterStudioImpl.java index 072c7c7d7..892188611 100644 --- a/lphy-studio/src/main/java/lphystudio/spi/LPhyStudioValueFormatterImpl.java +++ b/lphy-studio/src/main/java/lphystudio/spi/LPhyValueFormatterStudioImpl.java @@ -4,6 +4,7 @@ import lphy.core.spi.LPhyValueFormatter; import java.util.HashSet; +import java.util.Map; import java.util.Set; /** @@ -12,22 +13,32 @@ * It requires a public no-args constructor. * @author Walter Xie */ -public class LPhyStudioValueFormatterImpl implements LPhyValueFormatter { +public class LPhyValueFormatterStudioImpl implements LPhyValueFormatter { // List> valueFormatters = Arrays.asList( // AlignmentLog.class, TreeLog.class, VariableLog.class, VariableSummaryLog.class); /** * Required by ServiceLoader. */ - public LPhyStudioValueFormatterImpl() { + public LPhyValueFormatterStudioImpl() { } @Override - public Set> getValueFormatters() { + public Set> declareValueFormatters() { return new HashSet<>(); } + + @Override + public Map, Set>> getValueFormatters() { + return null; + } // public Map, Set>> getValueFormatterMap() { // return new HashMap<>(); // } + @Override + public void register() { + + } + public String getExtensionName() { return "LPhy studio loggers"; } diff --git a/lphy/src/main/java/lphy/core/spi/Extension.java b/lphy/src/main/java/lphy/core/spi/Extension.java index a352e168a..041bd3eb1 100644 --- a/lphy/src/main/java/lphy/core/spi/Extension.java +++ b/lphy/src/main/java/lphy/core/spi/Extension.java @@ -1,7 +1,22 @@ package lphy.core.spi; +/** + * The service interface defined for SPI. + * Implement this interface to create one "Container" provider class + * for each module of LPhy or its extensions. + * @author Walter Xie + */ public interface Extension { + /** + * The main method to register all classes and save them into a Map or Set, + * which will be used by {@link LoaderManager} to create their instances. + * The process will be different and related to the roles of these extensions doing, + * such as registering BasicFunction or GenerativeDistribution, and resolving ValueFormatter, etc. + */ + void register(); + + default String getModuleName() { Module module = getClass().getModule(); return module.getName(); diff --git a/lphy/src/main/java/lphy/core/spi/LPhyCoreImpl.java b/lphy/src/main/java/lphy/core/spi/LPhyCoreImpl.java index 3ea161936..9448836ef 100644 --- a/lphy/src/main/java/lphy/core/spi/LPhyCoreImpl.java +++ b/lphy/src/main/java/lphy/core/spi/LPhyCoreImpl.java @@ -2,6 +2,8 @@ import lphy.core.model.BasicFunction; import lphy.core.model.GenerativeDistribution; +import lphy.core.model.GeneratorUtils; +import lphy.core.model.NarrativeUtils; import lphy.core.parser.function.MapFunction; import lphy.core.simulator.Simulate; import lphy.core.vectorization.operation.ElementsAt; @@ -9,9 +11,7 @@ import lphy.core.vectorization.operation.Slice; import lphy.core.vectorization.operation.SliceDoubleArray; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; /** * The "Container" provider class that implements SPI @@ -21,25 +21,97 @@ */ public class LPhyCoreImpl implements LPhyExtension { - List> functions = Arrays.asList( - Range.class, Slice.class, SliceDoubleArray.class, ElementsAt.class, - Simulate.class, MapFunction.class); + //+++ fill in these two getter for the registration process +++// + @Override + public List> declareDistributions() { + return new ArrayList<>(); + } + + @Override + public List> declareFunctions() { + return Arrays.asList( + Range.class, Slice.class, SliceDoubleArray.class, ElementsAt.class, + Simulate.class, MapFunction.class); + } /** * Required by ServiceLoader. */ - public LPhyCoreImpl() { - //TODO do something here, e.g. print package or classes info ? + public LPhyCoreImpl() { } + + /** + * Key is the class name, values are (overloading) classes using this name. + * {@link GenerativeDistribution} + */ + protected Map>> genDistDictionary; + /** + * Key is the class name, values are (overloading) classes using this name. + * {@link BasicFunction} + */ + protected Map>> functionDictionary; + /** + * LPhy data types + */ + protected TreeSet> types; + + + @Override + public void register() { + System.out.println("Registering extension : " + this.getClass().getName()); + + genDistDictionary = new TreeMap<>(); + functionDictionary = new TreeMap<>(); +// dataTypeMap = new ConcurrentHashMap<>(); + types = new TreeSet<>(Comparator.comparing(Class::getName)); + + // GenerativeDistribution + List> genDist = declareDistributions(); + + for (Class genClass : genDist) { + String name = GeneratorUtils.getGeneratorName(genClass); + + Set> genDistSet = genDistDictionary.computeIfAbsent(name, k -> new HashSet<>()); + genDistSet.add(genClass); + // collect LPhy data types from GenerativeDistribution + types.add(GeneratorUtils.getReturnType(genClass)); + Collections.addAll(types, NarrativeUtils.getParameterTypes(genClass, 0)); + Collections.addAll(types, GeneratorUtils.getReturnType(genClass)); + } +// for (Class genClass : lightWeightGenClasses) { +// String name = Generator.getGeneratorName(genClass); +// +// Set> genDistSet = genDistDictionary.computeIfAbsent(name, k -> new HashSet<>()); +// genDistSet.add(genClass); +// types.add(LGenerator.getReturnType((Class)genClass)); +// } + // Func + List> funcs = declareFunctions(); + + for (Class functionClass : funcs) { + String name = GeneratorUtils.getGeneratorName(functionClass); + + Set> funcSet = functionDictionary.computeIfAbsent(name, k -> new HashSet<>()); + funcSet.add(functionClass); + // collect LPhy data types from Func + Collections.addAll(types, NarrativeUtils.getParameterTypes(functionClass, 0)); + Collections.addAll(types, GeneratorUtils.getReturnType(functionClass)); + } + } @Override - public List> getDistributions() { - return new ArrayList<>(); + public Map>> getDistributions() { + return genDistDictionary; + } + + @Override + public Map>> getFunctions() { + return functionDictionary; } @Override - public List> getFunctions() { - return functions; + public TreeSet> getTypes() { + return types; } public String getExtensionName() { diff --git a/lphy/src/main/java/lphy/core/spi/LPhyCoreLoader.java b/lphy/src/main/java/lphy/core/spi/LPhyCoreLoader.java index e033d0029..929825c0b 100644 --- a/lphy/src/main/java/lphy/core/spi/LPhyCoreLoader.java +++ b/lphy/src/main/java/lphy/core/spi/LPhyCoreLoader.java @@ -1,13 +1,8 @@ package lphy.core.spi; import lphy.core.logger.LoggerUtils; -import lphy.core.model.BasicFunction; -import lphy.core.model.GenerativeDistribution; -import lphy.core.model.GeneratorUtils; -import lphy.core.model.NarrativeUtils; import java.util.*; -import java.util.stream.Collectors; /** * The implementation to load LPhy extensions using {@link ServiceLoader}. @@ -17,122 +12,198 @@ * @author Walter Xie */ public class LPhyCoreLoader { - private ServiceLoader loader; - // Required by ServiceLoader - public LPhyCoreLoader() { } + private static ServiceLoader loader; - /** - * Key is the class name, values are (overloading) classes using this name. - * {@link GenerativeDistribution} - */ - public Map>> genDistDictionary; - /** - * Key is the class name, values are (overloading) classes using this name. - * {@link BasicFunction} - */ - public Map>> functionDictionary; - /** - * LPhy data types - */ - public TreeSet> types; + private Map extensionMap; - - /** - * The method to load all classes registered by SPI mechanism. - */ - public void loadAllExtensions() { + // Loader should be only called once; + public LPhyCoreLoader() { if (loader == null) - loader = ServiceLoader.load(LPhyExtension.class); + loader = ServiceLoader.load(Extension.class); - registerExtensions(null); + extensionMap = new TreeMap<>(); } - // if extClsName is null, then load all classes, - // otherwise load classes in a given extension. - private void registerExtensions(String extClsName) { - - genDistDictionary = new TreeMap<>(); - functionDictionary = new TreeMap<>(); -// dataTypeMap = new ConcurrentHashMap<>(); - types = new TreeSet<>(Comparator.comparing(Class::getName)); - + /** + * The method to load classes in a given {@link Extension} registered by SPI mechanism, + * and then call {@link Extension#register()}. + */ + public void loadExtensions() { //*** LPhyExtensionImpl must have a public no-args constructor ***// - Iterator extensions = loader.iterator(); + Iterator extensions = loader.iterator(); while (extensions.hasNext()) { - LPhyExtension lPhyExt = null; + Extension ext = null; try { //*** LPhyExtensionImpl must have a public no-args constructor ***// - lPhyExt = extensions.next(); + ext = extensions.next(); } catch (ServiceConfigurationError serviceError) { - System.err.println(serviceError.getMessage()); - } - // extClsName == null then register all - if (lPhyExt != null && (extClsName == null || lPhyExt.getClass().getName().equalsIgnoreCase(extClsName))) { - System.out.println("Registering extension from " + lPhyExt.getClass().getName()); - - // GenerativeDistribution - List> genDist = lPhyExt.getDistributions(); - - for (Class genClass : genDist) { - String name = GeneratorUtils.getGeneratorName(genClass); - - Set> genDistSet = genDistDictionary.computeIfAbsent(name, k -> new HashSet<>()); - genDistSet.add(genClass); - // collect LPhy data types from GenerativeDistribution - types.add(GeneratorUtils.getReturnType(genClass)); - Collections.addAll(types, NarrativeUtils.getParameterTypes(genClass, 0)); - Collections.addAll(types, GeneratorUtils.getReturnType(genClass)); - } -// for (Class genClass : lightWeightGenClasses) { -// String name = Generator.getGeneratorName(genClass); -// -// Set> genDistSet = genDistDictionary.computeIfAbsent(name, k -> new HashSet<>()); -// genDistSet.add(genClass); -// types.add(LGenerator.getReturnType((Class)genClass)); -// } - // Func - List> funcs = lPhyExt.getFunctions(); - - for (Class functionClass : funcs) { - String name = GeneratorUtils.getGeneratorName(functionClass); - - Set> funcSet = functionDictionary.computeIfAbsent(name, k -> new HashSet<>()); - funcSet.add(functionClass); - // collect LPhy data types from Func - Collections.addAll(types, NarrativeUtils.getParameterTypes(functionClass, 0)); - Collections.addAll(types, GeneratorUtils.getReturnType(functionClass)); - } - - // sequence types -// Map newDataTypes = lPhyExt.getSequenceTypes(); -// if (newDataTypes != null) -// // TODO validate same sequence type? -// newDataTypes.forEach(dataTypeMap::putIfAbsent); + LoggerUtils.log.severe(serviceError.getMessage()); + serviceError.printStackTrace(); + return; } + + String fullName = ext.getExtensionName(); + if (extensionMap.containsKey(fullName)) + throw new IllegalArgumentException("The extension " + fullName + + " has been loaded, but another same extension is requiring to register again ! " + + Arrays.toString(extensionMap.keySet().toArray())); + + System.out.println("Registering extension from " + ext.getClass().getName()); + + ext.register(); // fill in the dictionary or collect all types here + extensionMap.put(fullName, ext); } - System.out.println("\nGenerativeDistribution : " + Arrays.toString(genDistDictionary.keySet().toArray())); - System.out.println("Functions : " + Arrays.toString(functionDictionary.keySet().toArray())); - // for non-module release - if (genDistDictionary.size() < 1 || functionDictionary.size() < 1) - LoggerUtils.log.warning("LPhy base or equivalent lib was not loaded ! "); + } - TreeSet typeNames = types.stream().map(Class::getSimpleName).collect(Collectors.toCollection(TreeSet::new)); - System.out.println("LPhy data types : " + typeNames); -// System.out.println("LPhy sequence types : " + Arrays.toString(dataTypeMap.values().toArray(new SequenceType[0]))); + public Map getExtensionMap() { + return extensionMap; + } + /** + * @param extClsNameList Element is the fully qualified class name of the class + * that implements {@link Extension}, such as lphy.spi.LPhyCoreImpl, + * and contains all classes registered for SPI mechanism, + * such as BasicFunction or GenerativeDistribution. + * @return The only extensions requested by the list of their class names. + */ + public Map getExtensionMap(List extClsNameList) { + Map copy = new TreeMap<> (extensionMap); + // Retains only the elements in this set that are contained in the specified collection + copy.keySet().retainAll(extClsNameList); + return copy; } + /** + * @param extParentCls the parent class or interface + * @return The filtered extensions which are inherited from extParentCls. + */ + public Map getExtensionMap(Class extParentCls) { + Map extMap = new TreeMap<> (); + for (Map.Entry entry : extensionMap.entrySet()) { + // extParentCls is either the same as, or is a superclass or superinterface of entry.getValue() + if (extParentCls.isAssignableFrom(entry.getValue().getClass())) { + extMap.put(entry.getKey(), entry.getValue()); + } + } + return extMap; + } + + +// /** +// * Key is the class name, values are (overloading) classes using this name. +// * {@link GenerativeDistribution} +// */ +// public Map>> genDistDictionary; +// /** +// * Key is the class name, values are (overloading) classes using this name. +// * {@link BasicFunction} +// */ +// public Map>> functionDictionary; +// /** +// * LPhy data types +// */ +// public TreeSet> types; + +// +// /** +// * The method to load all classes registered by SPI mechanism. +// */ +// public void loadAllExtensions() { +// if (loader == null) +// loader = ServiceLoader.load(LPhyExtension.class); +// +// registerExtensions(null); +// } + + // if extClsName is null, then load all classes, + // otherwise load classes in a given extension. +// private void registerExtensions(List extClsNameList) { +// +// genDistDictionary = new TreeMap<>(); +// functionDictionary = new TreeMap<>(); +//// dataTypeMap = new ConcurrentHashMap<>(); +// types = new TreeSet<>(Comparator.comparing(Class::getName)); +// +// +// //*** LPhyExtensionImpl must have a public no-args constructor ***// +// Iterator extensions = loader.iterator(); +// +// while (extensions.hasNext()) { +// LPhyExtension lPhyExt = null; +// try { +// //*** LPhyExtensionImpl must have a public no-args constructor ***// +// lPhyExt = extensions.next(); +// } catch (ServiceConfigurationError serviceError) { +// System.err.println(serviceError.getMessage()); +// } +// // extClsName == null then register all +// if (lPhyExt != null && (extClsName == null || lPhyExt.getClass().getName().equalsIgnoreCase(extClsName))) { +// System.out.println("Registering extension from " + lPhyExt.getClass().getName()); +// +// // GenerativeDistribution +// List> genDist = lPhyExt.getDistributions(); +// +// for (Class genClass : genDist) { +// String name = GeneratorUtils.getGeneratorName(genClass); +// +// Set> genDistSet = genDistDictionary.computeIfAbsent(name, k -> new HashSet<>()); +// genDistSet.add(genClass); +// // collect LPhy data types from GenerativeDistribution +// types.add(GeneratorUtils.getReturnType(genClass)); +// Collections.addAll(types, NarrativeUtils.getParameterTypes(genClass, 0)); +// Collections.addAll(types, GeneratorUtils.getReturnType(genClass)); +// } +//// for (Class genClass : lightWeightGenClasses) { +//// String name = Generator.getGeneratorName(genClass); +//// +//// Set> genDistSet = genDistDictionary.computeIfAbsent(name, k -> new HashSet<>()); +//// genDistSet.add(genClass); +//// types.add(LGenerator.getReturnType((Class)genClass)); +//// } +// // Func +// List> funcs = lPhyExt.getFunctions(); +// +// for (Class functionClass : funcs) { +// String name = GeneratorUtils.getGeneratorName(functionClass); +// +// Set> funcSet = functionDictionary.computeIfAbsent(name, k -> new HashSet<>()); +// funcSet.add(functionClass); +// // collect LPhy data types from Func +// Collections.addAll(types, NarrativeUtils.getParameterTypes(functionClass, 0)); +// Collections.addAll(types, GeneratorUtils.getReturnType(functionClass)); +// } +// +// // sequence types +//// Map newDataTypes = lPhyExt.getSequenceTypes(); +//// if (newDataTypes != null) +//// // TODO validate same sequence type? +//// newDataTypes.forEach(dataTypeMap::putIfAbsent); +// } +// } +// +// System.out.println("\nGenerativeDistribution : " + Arrays.toString(genDistDictionary.keySet().toArray())); +// System.out.println("Functions : " + Arrays.toString(functionDictionary.keySet().toArray())); +// // for non-module release +// if (genDistDictionary.size() < 1 || functionDictionary.size() < 1) +// LoggerUtils.log.warning("LPhy base or equivalent lib was not loaded ! "); +// +// TreeSet typeNames = types.stream().map(Class::getSimpleName).collect(Collectors.toCollection(TreeSet::new)); +// System.out.println("LPhy data types : " + typeNames); +//// System.out.println("LPhy sequence types : " + Arrays.toString(dataTypeMap.values().toArray(new SequenceType[0]))); +// +// } + /** * The method to load classes in a given extension registered by SPI mechanism. * @param extClsName The fully qualified class name of the class that implements * {@link LPhyExtension}, such as lphy.spi.LPhyCoreImpl, * and contains all classes registered for SPI mechanism, * such as BasicFunction or GenerativeDistribution. - */ + public void loadExtension(String extClsName) { if (loader == null) loader = ServiceLoader.load(LPhyExtension.class); @@ -144,7 +215,7 @@ public void loadExtension(String extClsName) { /** * for extension manager. * @return a list of detected {@link LPhyExtension}. - */ + public List getExtensions() { if (loader == null) loader = ServiceLoader.load(LPhyExtension.class); @@ -155,5 +226,5 @@ public List getExtensions() { extensions.forEachRemaining(extList::add); return extList; } - + */ } diff --git a/lphy/src/main/java/lphy/core/spi/LPhyCoreValueFormatterImpl.java b/lphy/src/main/java/lphy/core/spi/LPhyCoreValueFormatterImpl.java deleted file mode 100644 index 6c5e8eef0..000000000 --- a/lphy/src/main/java/lphy/core/spi/LPhyCoreValueFormatterImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -package lphy.core.spi; - -import lphy.core.logger.ValueFormatter; - -import java.util.HashSet; -import java.util.Set; - -/** - * The "Container" provider class that implements SPI - * which include a list of {@link ValueFormatter} required in the core. - * It requires a public no-args constructor. - * @author Walter Xie - */ -public class LPhyCoreValueFormatterImpl implements LPhyValueFormatter { - - /** - * Required by ServiceLoader. - */ - public LPhyCoreValueFormatterImpl() { } - - @Override - public Set> getValueFormatters() { -// return Set.of(ValueFormatter.Base.class); - return new HashSet<>(); - } - - - public String getExtensionName() { - return "LPhy core loggers"; - } -} diff --git a/lphy/src/main/java/lphy/core/spi/LPhyExtension.java b/lphy/src/main/java/lphy/core/spi/LPhyExtension.java index 5ab97050b..fba85d262 100644 --- a/lphy/src/main/java/lphy/core/spi/LPhyExtension.java +++ b/lphy/src/main/java/lphy/core/spi/LPhyExtension.java @@ -4,12 +4,13 @@ import lphy.core.model.GenerativeDistribution; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; /** - * The service interface defined for SPI. - * Implement this interface to create one "Container" provider class - * for each module of LPhy or its extensions, - * which should include {@link GenerativeDistribution}, {@link BasicFunction}. + * The interface to define the registration of + * {@link GenerativeDistribution}, {@link BasicFunction}. * * @author Walter Xie */ @@ -18,11 +19,18 @@ public interface LPhyExtension extends Extension { /** * @return the list of new {@link GenerativeDistribution} implemented in the LPhy extension. */ - List> getDistributions(); + List> declareDistributions(); /** * @return the list of new {@link BasicFunction} implemented in the LPhy extension. */ - List> getFunctions(); + List> declareFunctions(); + + + Map>> getDistributions(); + + Map>> getFunctions(); + + TreeSet> getTypes(); } diff --git a/lphy/src/main/java/lphy/core/spi/LPhyValueFormatter.java b/lphy/src/main/java/lphy/core/spi/LPhyValueFormatter.java index e48f53d4f..18a87c5c9 100644 --- a/lphy/src/main/java/lphy/core/spi/LPhyValueFormatter.java +++ b/lphy/src/main/java/lphy/core/spi/LPhyValueFormatter.java @@ -2,6 +2,7 @@ import lphy.core.logger.ValueFormatter; +import java.util.Map; import java.util.Set; /** @@ -15,10 +16,10 @@ public interface LPhyValueFormatter extends Extension { - Set> getValueFormatters(); - -// List> getSimulatorListenerClasses(); + Set> declareValueFormatters(); +// List> declareSimulatorListeners(); + Map, Set>> getValueFormatters(); } diff --git a/lphy/src/main/java/lphy/core/spi/LPhyValueFormatterCoreImpl.java b/lphy/src/main/java/lphy/core/spi/LPhyValueFormatterCoreImpl.java new file mode 100644 index 000000000..8d0bb8fb9 --- /dev/null +++ b/lphy/src/main/java/lphy/core/spi/LPhyValueFormatterCoreImpl.java @@ -0,0 +1,89 @@ +package lphy.core.spi; + +import lphy.core.logger.LoggerUtils; +import lphy.core.logger.ValueFormatter; +import lphy.core.model.GeneratorUtils; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * The "Container" provider class that implements SPI + * which include a list of {@link ValueFormatter} required in the core. + * It requires a public no-args constructor. + * @author Walter Xie + */ +public class LPhyValueFormatterCoreImpl implements LPhyValueFormatter { + + @Override + public Set> declareValueFormatters() { +// return Set.of(ValueFormatter.Base.class); + return new HashSet<>(); + } + + /** + * Required by ServiceLoader. + */ + public LPhyValueFormatterCoreImpl() { } + + /** + * Key is data type, e.g. Integer.class, value is the Set of ValueFormatter assigned to this type. + */ + protected Map, Set>> valueFormatterClasses; + + + @Override + public void register() { + System.out.println("Registering extension : " + this.getClass().getName()); + + valueFormatterClasses = new HashMap<>(); + + // ValueFormatter + Set> formatterSet = declareValueFormatters(); + + //TODO better code ? +// for (Map.Entry, Set>> entry : formatterMap.entrySet()) { + for (Class vFCls : formatterSet) { + // get the data type + // Class typeCls = vFCls.getTypeParameters()[0].getClass(); + Class typeCls = null; + try { + Method method = vFCls.getMethod("getDataTypeClass"); + typeCls = GeneratorUtils.getGenericReturnType(method); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + if (typeCls == null) + LoggerUtils.log.severe("Extension " + getExtensionName() + + " : ValueFormatter '" + vFCls.getName() + "' cannot find data type !"); + + Set> valFormSet = valueFormatterClasses + .computeIfAbsent(typeCls, k -> new HashSet<>()); +// Set> newValFormSet = entry.getValue(); + + // warning, if the same ValueFormatter exists for the same Class in valueFormatterClasses map +// newValFormSet.forEach(dataType -> { + if (valFormSet.contains(vFCls)) + LoggerUtils.log.warning("Extension " + getExtensionName() + + " : ValueFormatter '" + vFCls.getName() + "' already exists in ValueFormatter map !"); + valFormSet.add(vFCls); +// }); + + } + // SimulatorListener +// List> listeners = valueFormatterSPI.getSimulatorListenerClasses(); +// LoaderManager.registerClasses(listeners, simulatorListeners); + + } + + public Map, Set>> getValueFormatters() { + return valueFormatterClasses; + } + + public String getExtensionName() { + return "LPhy core loggers"; + } +} diff --git a/lphy/src/main/java/lphy/core/spi/LoaderManager.java b/lphy/src/main/java/lphy/core/spi/LoaderManager.java index c9bb38378..2dc4dcc91 100644 --- a/lphy/src/main/java/lphy/core/spi/LoaderManager.java +++ b/lphy/src/main/java/lphy/core/spi/LoaderManager.java @@ -1,24 +1,26 @@ package lphy.core.spi; +import lphy.core.logger.LoggerUtils; import lphy.core.logger.ValueFormatResolver; import lphy.core.logger.ValueFormatter; import lphy.core.model.DeterministicFunction; import lphy.core.model.GenerativeDistribution; import java.util.*; +import java.util.stream.Collectors; /** * Load all loaders here */ public class LoaderManager { - private static Map>> genDistDictionary; - private static Map>> functionDictionary; + private static Map>> genDistDictionary = new TreeMap<>(); + private static Map>> functionDictionary = new TreeMap<>(); + private static TreeSet> types = new TreeSet<>(Comparator.comparing(Class::getName)); + private static Set bivarOperators; private static Set univarfunctions; - private static TreeSet> types;// = new TreeSet<>(Comparator.comparing(Class::getName)); - private static LPhyCoreLoader lphyCoreLoader = new LPhyCoreLoader(); // TODO handle resolve strategies here? @@ -28,19 +30,33 @@ public class LoaderManager { static { // registration process - lphyCoreLoader.loadAllExtensions(); - - genDistDictionary = lphyCoreLoader.genDistDictionary; - functionDictionary = lphyCoreLoader.functionDictionary; + lphyCoreLoader.loadExtensions(); - types = lphyCoreLoader.types; + collectAllRegisteredClasses(); + report(); + } - ValueFormatterLoader lphyValueFormatterLoader = new ValueFormatterLoader(); - lphyValueFormatterLoader.loadAllExtensions(); - Map, Set>> valueFormatters = - lphyValueFormatterLoader.getValueFormattersClasses(); - // pass all ValueFormatter classes to the Resolver - valueFormatResolver = new ValueFormatResolver(valueFormatters); + static void collectAllRegisteredClasses() { + + Map extMap = lphyCoreLoader.getExtensionMap(); + // loop through all extesions + for (Map.Entry entry : extMap.entrySet()) { + Extension extension = entry.getValue(); + if (LPhyExtension.class.isAssignableFrom(extension.getClass())) { + // {@link GenerativeDistribution}, {@link BasicFunction}. + genDistDictionary.putAll(((LPhyExtension) extension).getDistributions()); + functionDictionary.putAll(((LPhyExtension) extension).getFunctions()); + types.addAll(((LPhyExtension) extension).getTypes()); + } else if (LPhyValueFormatter.class.isAssignableFrom(extension.getClass())) { + // + Map, Set>> valueFormatters = + ((LPhyValueFormatter) extension).getValueFormatters(); + // pass all ValueFormatter classes to the Resolver + valueFormatResolver = new ValueFormatResolver(valueFormatters); + } else { + System.out.println("Unsovled extension : " + extension.getExtensionName()); + } + } bivarOperators = new HashSet<>(); for (String s : new String[]{"+", "-", "*", "/", "**", "&&", "||", "<=", "<", ">=", ">", "%", ":", "^", "!=", "==", "&", "|", "<<", ">>", ">>>"}) { @@ -51,11 +67,21 @@ public class LoaderManager { univarfunctions.add(s); } - // register data types -// Map dataTypeMap = lphyLoader.dataTypeMap; -// SequenceTypeFactory.INSTANCE.setDataTypeMap(dataTypeMap); } + static void report() { + System.out.println("\nGenerativeDistribution : " + Arrays.toString(genDistDictionary.keySet().toArray())); + System.out.println("Functions : " + Arrays.toString(functionDictionary.keySet().toArray())); + // for non-module release + if (genDistDictionary.size() < 1 || functionDictionary.size() < 1) + LoggerUtils.log.warning("LPhy base or equivalent lib was not loaded ! "); + + TreeSet typeNames = types.stream().map(Class::getSimpleName).collect(Collectors.toCollection(TreeSet::new)); + System.out.println("LPhy data types : " + typeNames); +// System.out.println("LPhy sequence types : " + Arrays.toString(dataTypeMap.values().toArray(new SequenceType[0]))); + } + + public LoaderManager() { } diff --git a/lphy/src/main/java/lphy/core/spi/ValueFormatterLoader.java b/lphy/src/main/java/lphy/core/spi/ValueFormatterLoader.java index df0ea395c..fc001b92d 100644 --- a/lphy/src/main/java/lphy/core/spi/ValueFormatterLoader.java +++ b/lphy/src/main/java/lphy/core/spi/ValueFormatterLoader.java @@ -14,6 +14,7 @@ * * @author Walter Xie */ +@Deprecated public class ValueFormatterLoader { private ServiceLoader loader; @@ -46,7 +47,7 @@ private void registerExtensions(String extClsName) { // simulatorListeners = new HashMap<>(); - //*** SequenceTypeExtensionImpl must have a public no-args constructor ***// + //*** LPhyValueFormatterCoreImpl must have a public no-args constructor ***// Iterator extensions = loader.iterator(); while (extensions.hasNext()) { @@ -64,7 +65,7 @@ private void registerExtensions(String extClsName) { if (extClsName == null || valueFormatterSPI.getClass().getName().equalsIgnoreCase(extClsName)) { System.out.println("Registering extension from " + valueFormatterSPI.getClass().getName()); // ValueFormatter - Set> formatterSet = valueFormatterSPI.getValueFormatters(); + Set> formatterSet = valueFormatterSPI.declareValueFormatters(); //TODO better code ? // for (Map.Entry, Set>> entry : formatterMap.entrySet()) { diff --git a/lphy/src/main/java/module-info.java b/lphy/src/main/java/module-info.java index cc4ecbbf8..f4239f788 100644 --- a/lphy/src/main/java/module-info.java +++ b/lphy/src/main/java/module-info.java @@ -1,5 +1,5 @@ import lphy.core.spi.LPhyCoreImpl; -import lphy.core.spi.LPhyCoreValueFormatterImpl; +import lphy.core.spi.LPhyValueFormatterCoreImpl; /** * @author Walter Xie @@ -46,10 +46,7 @@ opens lphy.core.simulator; // LPhy extensions - uses lphy.core.spi.LPhyExtension; + uses lphy.core.spi.Extension; // declare what service interface the provider intends to use - provides lphy.core.spi.LPhyExtension with LPhyCoreImpl; - - uses lphy.core.spi.LPhyValueFormatter; - provides lphy.core.spi.LPhyValueFormatter with LPhyCoreValueFormatterImpl; + provides lphy.core.spi.Extension with LPhyCoreImpl, LPhyValueFormatterCoreImpl; } \ No newline at end of file