/* * Copyright 2014 Igor Maznitsa (http://www.igormaznitsa.com). * * 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 com.igormaznitsa.prol.libraries; import com.igormaznitsa.prol.annotations.ProlOperators; import com.igormaznitsa.prol.annotations.PredicateSynonyms; import com.igormaznitsa.prol.annotations.Predicate; import com.igormaznitsa.prol.annotations.ProlOperator; import com.igormaznitsa.prol.containers.OperatorContainer; import com.igormaznitsa.prol.data.Operator; import com.igormaznitsa.prol.data.TermStruct; import com.igormaznitsa.prol.exceptions.ProlCriticalError; import com.igormaznitsa.prol.logic.ProlContext; import com.igormaznitsa.prol.utils.Utils; import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; /** * The core class describes a prolog library contains predicates and operators. * Any class which contains predicates msut extend this abstract class. Only * classes extend this abstract class can be added as libraries in a prolog * engine context * * @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com) * @see com.igormaznitsa.prol.containers.KnowledgeBase * @see com.igormaznitsa.prol.logic.ProlContext */ public abstract class ProlAbstractLibrary { /** * The table contains all operators which have been defined in the library */ protected final Map<String, OperatorContainer> libraryOperators = new HashMap<String, OperatorContainer>(); /** * The variable contains the text representation of library UID */ protected final String libraryUID; /** * the table contains all predicate which have been defined in the library */ protected final Map<String, PredicateProcessor> predicateMethodsMap = new HashMap<String, PredicateProcessor>(); /** * Set for all zero arity predicate names */ protected final Set<String> zeroArityPredicateNames = new HashSet<String>(); /** * The constructor * * @param libraryID the ID for the library, must not be null */ public ProlAbstractLibrary(final String libraryID) { if (libraryID == null) { throw new IllegalArgumentException("The library ID must not be null!"); } this.libraryUID = libraryID; loadStaticOperators(); scanThisClassForPredicates(); } /** * Check that the library contains a predicate for a signature * * @param signature a predicate signature ("[FUNCTOR]/[ARITY]") * @return true if the library contains such predicate, else false */ public boolean hasPredicateForSignature(final String signature) { return this.predicateMethodsMap.containsKey(signature); } /** * Find a processor for a predicate * * @param predicate the predicate to find a predicate processor for it, must * not be null * @return found processor as a PredicateProcessor object else null * @see com.igormaznitsa.prol.libraries.PredicateProcessor */ public PredicateProcessor findProcessorForPredicate(final TermStruct predicate) { return this.predicateMethodsMap.get(predicate.getSignature()); } public boolean hasZeroArityPredicateForName(final String name) { return this.zeroArityPredicateNames.contains(name); } /** * get the ID of the library * * @return the library ID as String, must not be null */ public String getLibraryUID() { return this.libraryUID; } /** * Release the library. Release and clear resources taken by the library. */ public void release() { try { this.predicateMethodsMap.clear(); this.libraryOperators.clear(); this.zeroArityPredicateNames.clear(); } catch (Exception thr) { Logger.getLogger(this.getClass().getCanonicalName()).log(Level.WARNING, "release()", thr); } } @Override public int hashCode() { return this.libraryUID.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj instanceof ProlAbstractLibrary) { final ProlAbstractLibrary other = (ProlAbstractLibrary) obj; return this.libraryUID.equals(other.libraryUID); } return true; } /** * Inside auxulary function to scan a class for annotations and fill inside * tables with found contanent * * @see com.igormaznitsa.prol.annotations.Predicate * @see com.igormaznitsa.prol.annotations.PredicateSynonims * @see com.igormaznitsa.prol.annotations.Determined * @see com.igormaznitsa.prol.annotations.Evaluable */ private void scanThisClassForPredicates() { final Class<?> thisClass = this.getClass(); final Method[] methods = thisClass.getMethods(); this.predicateMethodsMap.clear(); for (int li = 0; li < methods.length; li++) { final Method method = methods[li]; final Predicate predicateAnnotation = method.getAnnotation(Predicate.class); final PredicateSynonyms synonims = method.getAnnotation(PredicateSynonyms.class); if (predicateAnnotation != null) { final String signature = Utils.normalizeSignature(predicateAnnotation.Signature()); if (signature == null) { throw new ProlCriticalError("Wrong signature of a predicate method " + method.getName() + " at " + libraryUID); } if (predicateMethodsMap.containsKey(signature)) { throw new ProlCriticalError("Duplicated predicate method " + signature + " at " + libraryUID); } PredicateTemplate[][] templates = null; final String[] templateStrings = predicateAnnotation.Template(); if (templateStrings != null && templateStrings.length > 0) { templates = new PredicateTemplate[templateStrings.length][]; for (int lt = 0; lt < templateStrings.length; lt++) { final String[] str = templateStrings[lt].split(","); PredicateTemplate[] curtemp = new PredicateTemplate[str.length]; for (int ld = 0; ld < str.length; ld++) { curtemp[ld] = new PredicateTemplate(str[ld]); } templates[lt] = curtemp; } } final PredicateProcessor processor = new PredicateProcessor(this, signature, method, templates); this.predicateMethodsMap.put(signature, processor); if (signature.endsWith("/0")) { this.zeroArityPredicateNames.add(signature.substring(0, signature.lastIndexOf('/'))); } if (synonims != null) { final String[] synonimSignatures = synonims.Signatures(); for (int lz = 0; lz < synonimSignatures.length; lz++) { final String sig = synonimSignatures[lz].trim(); this.predicateMethodsMap.put(sig, processor); if (sig.endsWith("/0")) { this.zeroArityPredicateNames.add(sig.substring(0, sig.lastIndexOf('/'))); } } } } } } /** * Add a static (system) operator into the library table * * @param operator an operator which will be added into the operator table as * a system operator, must not be null */ private void addStaticOperator(final ProlOperator operator) { Operator newOperator = new Operator(operator.Priority(), operator.Type(), operator.Name()); OperatorContainer container = libraryOperators.get(operator.Name()); if (container == null) { container = new OperatorContainer(newOperator, true); libraryOperators.put(operator.Name(), container); } else { container.setOperator(newOperator); } } /** * Scan class for operator anotations and place them into local tables * * @see com.igormaznitsa.prol.annotations.ProlOperator * @see com.igormaznitsa.prol.annotations.ProlOperators */ @SuppressWarnings("unchecked") private void loadStaticOperators() { libraryOperators.clear(); final Class<?> thisClass = this.getClass(); // find ProlOperators final ProlOperators operators = thisClass.getAnnotation(ProlOperators.class); if (operators != null) { ProlOperator[] operatorList = operators.Operators(); for (final ProlOperator lst : operatorList) { addStaticOperator(lst); } } // find ProlOperator final ProlOperator operator = thisClass.getAnnotation(ProlOperator.class); if (operator != null) { addStaticOperator(operator); } } /** * Check that the string is the name of a static (system) operator from the * library * * @param nameToBeChecked the name to be checked * @return if there is an operator with such name then true else false */ public boolean isSystemOperator(final String nameToBeChecked) { if (nameToBeChecked == null) { return false; } return libraryOperators.containsKey(nameToBeChecked); } /** * Check that the library has a system operator whose name starts with a * substring * * @param startSubstring the substring to be checked, must not be null * @return true if there is an operator whose name has start with the * substring, else null */ public boolean hasSyatemOperatorStartsWith(final String startSubstring) { final Iterator<String> operators = libraryOperators.keySet().iterator(); while (operators.hasNext()) { if (operators.next().startsWith(startSubstring)) { return true; } } return false; } /** * Find an operator for full name * * @param operatorName the name of needed operator, must not be null * @return found operator if it has been found else null */ public OperatorContainer findSystemOperatorForName(final String operatorName) { return libraryOperators.get(operatorName); } /** * The notification that the context (which is the owner for the library * instance) is being halted * * @param context the prol context must not be null */ public void contextHasBeenHalted(final ProlContext context) { } }