All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.boreal.io.dlgp.DlgpWriter Maven / Gradle / Ivy

The newest version!
package fr.boreal.io.dlgp;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import fr.boreal.io.api.Writer;
import fr.boreal.model.formula.api.FOFormula;
import fr.boreal.model.formula.api.FOFormulaConjunction;
import fr.boreal.model.formula.api.FOFormulaDisjunction;
import fr.boreal.model.formula.api.FOFormulaNegation;
import fr.boreal.model.kb.api.FactBase;
import fr.boreal.model.kb.api.KnowledgeBase;
import fr.boreal.model.kb.api.RuleBase;
import fr.boreal.model.logicalElements.api.Atom;
import fr.boreal.model.logicalElements.api.Constant;
import fr.boreal.model.logicalElements.api.FunctionalTerm;
import fr.boreal.model.logicalElements.api.Literal;
import fr.boreal.model.logicalElements.api.Predicate;
import fr.boreal.model.logicalElements.api.Substitution;
import fr.boreal.model.logicalElements.api.Term;
import fr.boreal.model.logicalElements.api.Variable;
import fr.boreal.model.query.api.FOQuery;
import fr.boreal.model.query.impl.UnionFOQuery;
import fr.boreal.model.rule.api.FORule;

/**
 * Writes the content of the knowledge base as DLGP
 * 
 * @author Florent Tornil
 */
public class DlgpWriter implements Writer {

	private final java.io.Writer writer;

	private boolean needLineBreak = false;

	/**
	 * Default constructor writing in the standard output
	 */
	public DlgpWriter() {
		this(new OutputStreamWriter(System.out) {
			public void close() throws IOException {
				// Do nothing (do not close System.out)
				this.flush();
			}
		});
	}

	/**
	 * Constructor writing with the given writer
	 * @param writer the writer to use
	 */
	public DlgpWriter(java.io.Writer writer) {
		this.writer = writer;
	}

	/**
	 * @param file file to write into
	 * @throws IOException  if the file exists but is a directory rather than
	 *                  a regular file, does not exist but cannot be created,
	 *                  or cannot be opened for any other reason
	 */
	public DlgpWriter(File file) throws IOException {
		this(new FileWriter(file));
	}

	/**
	 * @param filepath filepath to write into
	 * @throws IOException  if the file path exists but is a directory rather than
	 *                  a regular file, does not exist but cannot be created,
	 *                  or cannot be opened for any other reason
	 */
	public DlgpWriter(String filepath) throws IOException {
		this(new File(filepath));
	}

	@Override
	public void close() throws IOException {
		this.writer.close();
	}

	@Override
	public void flush() throws IOException {
		this.writer.flush();
	}

	//
	// DLGP writing methods
	//

	@Override
	public void write(Object object) throws IOException {
        switch (object) {
            case Term term -> this.write(term);
            case KnowledgeBase knowledgeBase -> this.write(knowledgeBase);
            case FactBase factBase -> this.write(factBase);
            case RuleBase ruleBase -> this.write(ruleBase);
            case FORule foRule -> this.write(foRule);
            case FOQuery foQuery -> this.write(foQuery);
            case FOFormula foFormula -> this.write(foFormula);
            case Collection objects -> this.writeConjunction((Collection) objects);
            case Predicate predicate -> this.write(predicate);
            case String s -> this.write(s);
            case null, default ->
                    throw new IllegalArgumentException("The object " + object + " of type " + object.getClass().getSimpleName() + " is not serializable as DLGP");
        }
	}

	/**
	 * Write the given string
	 * @param str string to write
	 * @throws IOException If an I/O error occurs 
	 */
	public void write(String str) throws IOException {
		this.writer.write(str);
	}

	//
	// DLGP writing methods
	//

	/**
	 * Write the given kb as DLGP
	 * @param kb to write
	 * @throws IOException If an I/O error occurs 
	 */
	public void write(KnowledgeBase kb) throws IOException {
		if(this.needLineBreak) {
			this.write("\n");
			this.needLineBreak = false;
		}

		this.write(kb.getFactBase());
		this.write(kb.getRuleBase());
	}

	/**
	 * Write the given fb as DLGP
	 * @param fb to write
	 * @throws IOException If an I/O error occurs 
	 */
	public void write(FactBase fb) throws IOException {
		if(this.needLineBreak) {
			this.write("\n");
			this.needLineBreak = false;
		}

		if(fb.size() != 0) {
			this.write("@facts\n");

			List atoms = fb.getAtoms().collect(Collectors.toList());

			this.writeConjunction(atoms);
			this.write(".");
			this.needLineBreak = true;
		}
	}

	/**
	 * Write the given rb as DLGP
	 * @param rb to write
	 * @throws IOException If an I/O error occurs
	 */
	public void write(RuleBase rb) throws IOException {
		if(this.needLineBreak) {
			this.write("\n");
			this.needLineBreak = false;
		}

		if(!rb.getRules().isEmpty()) {
			this.write("@rules\n");
			for(FORule rule : rb.getRules()) {
				this.write(rule);
			}
		}
		this.needLineBreak = true;
	}

	/**
	 * Write the given rule as DLGP
	 * @param rule to write
	 * @throws IOException If an I/O error occurs
	 */
	public void write(FORule rule) throws IOException {
		if(this.needLineBreak) {
			this.write("\n");
			this.needLineBreak = false;
		}

		this.writeLabel(rule.getLabel());
		this.write(rule.getHead());
		this.write(" :- ");
		this.write(rule.getBody());
		this.write(".");
		this.needLineBreak = true;
	}

	/**
	 * Write the given query as DLGP
	 * @param query to write
	 * @throws IOException If an I/O error occurs 
	 */
	public void write(FOQuery query) throws IOException {
		if(query instanceof UnionFOQuery) {
			throw new UnsupportedOperationException("Union of queries are not supported by DLGP");
		}

		if(this.needLineBreak) {
			this.write("\n");
			this.needLineBreak = false;
		}

		this.writeLabel(query.getLabel());
		this.write("?");
		Collection ansVars = query.getAnswerVariables();
		this.write("(");
		if(!ansVars.isEmpty()) {
			boolean isFirst = true;
			for(Variable v : ansVars) {
				if(isFirst) {
					isFirst = false;
				} else {
					this.write(", ");
				}
				this.write(v);
			}
		}
		this.write(")");
		this.write(" :- ");
		this.write(query.getFormula());
		this.writeEqualities(query.getVariableEqualities().getAssociatedSubstitution(query).orElseThrow());
		this.write(".");
		this.needLineBreak = true;
	}

	/**
	 * Write the given formula as DLGP
	 * For now, only conjunction is handled (and flattened)
	 * @param formula to write
	 * @throws IOException If an I/O error occurs
	 */
	public void write(FOFormula formula) throws IOException {
		boolean isFirst = true;
		if(formula.isConjunction()) {
			for(FOFormula subformula : ((FOFormulaConjunction)formula).getSubElements()) {
				if(isFirst) {
					isFirst = false;
				} else {
					this.write(", ");
				}
				this.write(subformula);
			}
		} else if(formula.isDisjunction()) {
			for(FOFormula subformula : ((FOFormulaDisjunction)formula).getSubElements()) {
				if(isFirst) {
					isFirst = false;
				} else {
					this.write("| ");
				}
				this.write(subformula);
			}
		} else if(formula.isNegation()) {
			this.write("not(");
			this.write(((FOFormulaNegation)formula).element());
			this.write(")");
		} else if(formula.isAtomic()) {
			this.write((Atom)formula);
		}
	}

	/**
	 * Write the given atoms as a single conjunction as DLGP
	 * @param atoms conjunction to write
	 * @throws IOException If an I/O error occurs
	 */
	public void writeConjunction(Collection atoms) throws IOException {
		boolean isFirst = true;
		for(Atom atom : atoms) {
			if(isFirst) {
				isFirst = false;
			} else {
				this.write(", ");
			}
			this.write(atom);
		}
	}

	/**
	 * Write the given atom as DLGP
	 * @param atom to write
	 * @throws IOException If an I/O error occurs
	 */
	public void write(Atom atom) throws IOException {
		this.write(atom.getPredicate());
		this.write("(");

		boolean isFirst = true;
		for(Term t : atom.getTerms()) {
			if(isFirst) {
				isFirst = false;
			} else {
				this.write(", ");
			}
			this.write(t);
		}
		this.write(")");
	}

	/**
	 * Write the given predicate as DLGP
	 * @param p predicate to write
	 * @throws IOException If an I/O error occurs
	 */
	public void write(Predicate p) throws IOException {
		String s = p.label();
		if (DlgpGrammarUtils.checkLIdent(s)) {
			this.write(s);
		} else {
			this.write("<");
			this.write(encode(s));
			this.write(">");
		}
	}

	/**
	 * Write the given term as DLGP
	 * @param t term to write
	 * @throws IOException If an I/O error occurs
	 */
	public void write(Term t) throws IOException {
		if(t.isVariable()) {
			this.write((Variable)t);
		} else if(t.isLiteral()) {
			this.write((Literal)t);
		} else if(t.isFunctionalTerm()) {
			this.write((FunctionalTerm)t);
		} else { // Constant
			this.write((Constant)t);
		}
	}

	/**
	 * Write the given variable as DLGP
	 * @param v variable to write
	 * @throws IOException If an I/O error occurs
	 */
	public void write(Variable v) throws IOException {
		String s = v.label();
		char first = s.charAt(0);
		if (first < 'A' || first > 'Z') {
			s = "VAR_" + s;
		}
		s = s.replaceAll("[^a-zA-Z0-9_]", "_");
		this.write(s);
	}

	/**
	 * Write the given literal as DLGP.
	 * This is not yet fully implemented.
	 * 
* Actually handles Integer, Double and Boolean. * Other types are written as String. *
* In order to add more types, you can extend this method. * @param l literal to write * @throws IOException If an I/O error occurs */ public void write(Literal l) throws IOException { if (l.value() instanceof Integer) { this.write(l.value().toString()); } else if (l.value() instanceof Double) { this.write(l.value().toString()); } else if (l.value() instanceof Boolean) { this.write(l.value().toString()); } else { this.write("\""); this.write(l.value().toString().replaceAll("\"", "\\\\\"")); this.write("\""); } } /** * Write the given functional term as DLGP. * This is not yet fully implemented. *
* The computed prefix is not added back ! * * @param f functional term to write * @throws IOException If an I/O error occurs */ public void write(FunctionalTerm f) throws IOException { this.write(f.toString()); } /** * Write the given constant as DLGP * @param c constant to write * @throws IOException If an I/O error occurs */ public void write(Constant c) throws IOException { String s = c.label(); if (DlgpGrammarUtils.checkLIdent(s)) { this.write(s); } else { this.write("<"); this.write(encode(s)); this.write(">"); } } // // Helper methods // /** * Write the given DLGP label if it is not null or blank * @param label to write as DLGP * @throws IOException If an I/O error occurs */ private void writeLabel(String label) throws IOException { if(label != null && !label.isBlank()) { this.write("["); this.write(label); this.write("] "); } } /** * Write the given substitution as equalities * This is used to convert queries with a specialization into a dlgp query * Also assumes that this is at the end of a non-empty conjunction * @param specialization substitution to write as equalities * @throws IOException If an I/O error occurs */ private void writeEqualities(Substitution specialization) throws IOException { for(Variable v : specialization.keys()) { this.write(", "); this.write(v); this.write(" = "); this.write(specialization.createImageOf(v)); } } // // Old DLGP writer helper methods // private static final Map replacements = new HashMap<>(); private static final Pattern pattern; static { replacements.put("\\", "\\\\u00" + Integer.toHexString('\\')); replacements.put(" ", "\\\\u00" + Integer.toHexString(' ')); replacements.put("<", "\\\\u00" + Integer.toHexString('<')); replacements.put(">", "\\\\u00" + Integer.toHexString('>')); replacements.put("\"", "\\\\u00" + Integer.toHexString('"')); replacements.put("{", "\\\\u00" + Integer.toHexString('{')); replacements.put("}", "\\\\u00" + Integer.toHexString('}')); replacements.put("|", "\\\\u00" + Integer.toHexString('|')); replacements.put("^", "\\\\u00" + Integer.toHexString('^')); replacements.put("`", "\\\\u00" + Integer.toHexString('`')); StringBuilder regexp = new StringBuilder(); boolean first = true; for(String key : replacements.keySet()) { if(!first) { regexp.append('|'); } first = false; regexp.append("\\").append(key); } pattern = Pattern.compile(regexp.toString()); } /* * Replace some characters */ private static String encode(String s) { StringBuilder sb = new StringBuilder(); Matcher m = pattern.matcher(s); while (m.find()) { m.appendReplacement(sb, replacements.get(m.group())); } m.appendTail(sb); return sb.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy