package translation.tag_parser;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import translation.trees.AuxType;
import translation.trees.ElementaryTree;
import translation.trees.GornAddress;
import translation.trees.LexicalNode;
import translation.trees.Node;
import translation.trees.NodeType;
public class Item {
private ElementaryTree tree;
private GornAddress dot; // adresse du noeud où se trouve le point
private DotPos pos; // position du point par rapport au noeud
private int startState;
private Set<Item> contextItems;
// addresses où une adjonction est en cours
private List<GornAddress> halfAdjoinedAddr;
private List<Item> halfAdjoinedItems; // items en cours d'adjonction
public Item (ElementaryTree tree, GornAddress dot,
DotPos pos, int startState) {
this.tree = tree;
this.dot = dot;
this.pos = pos;
this.startState = startState;
this.contextItems = new HashSet<Item>();
this.halfAdjoinedAddr = new ArrayList<GornAddress>(1);
this.halfAdjoinedItems = new ArrayList<Item>(1);
}
public Item (ElementaryTree tree, GornAddress dot, DotPos pos,
int startState, Set<Item> contextItems,
List<Item> halfAdjoined, List<GornAddress> halfAdjoinedAddr) {
this(tree, dot, pos, startState);
this.contextItems = contextItems;
this.halfAdjoinedItems = halfAdjoined;
this.halfAdjoinedAddr = halfAdjoinedAddr;
}
private boolean raRoot() {
return (getPos() == DotPos.RIGHT_ABOVE &&
getDottedNode().isRoot());
}
/* Gardes des règles */
/*
* Décrit un item potentiellement "acceptant", qui
* sera effectivement "acceptant" s'il n'y a plus de
* mot à reconnaître en entrée, et alors l'analyse
* réussit.
*
* Seuls les arbres initiaux, pas auxiliaires,
* peuvent donner lieu à l'acceptation.
*
* Pour accepter même des phrases plus ou moins incomplètes,
* il suffira de donner en argument à buildInitialItems
* (qui est chargée de construire les arbres de départ
* de l'état initial, qui correspondent aux phrases qui
* seront acceptées) la liste des catégories de noeuds
* traités comme racines de départ.
*/
public boolean potentialAcceptationItem() {
return raRoot() &&
// l'arbre n'a ni été adjoint ni substitué,
// il existe donc donc depuis le tout début
// et correspond donc à une possible acceptation.
contextItems.isEmpty();
}
public boolean canMoveDown() {
return ((! getDottedNode().getSons().isEmpty()) &&
getPos() == DotPos.LEFT_ABOVE);
}
public boolean canMoveRight() {
return (getDottedNode().nextBrother() != null &&
getPos() == DotPos.RIGHT_ABOVE);
}
/*
* D'abord tester avec canMoveRight
* si on peut se déplacer à droite.
*/
public boolean canMoveUp() {
return (getDottedNode().getFather() != null &&
getPos() == DotPos.RIGHT_ABOVE);
}
public boolean canPredictSubst() {
return (getDottedNode().isSubst() &&
getPos() == DotPos.LEFT_ABOVE);
}
public boolean canCompleteSubst() {
return (raRoot() &&
(! getTree().isAuxiliary()) &&
// le différentie d'un item potentiellement
// acceptant.
(! contextItems.isEmpty()));
}
/*
* Aux noeuds lexicalisés et de substitution
* on ne doit pas pouvoir adjoindre mais on
* peut aux racines, ce qui revient au même que
* si c'était aux noeuds de substitution mais qui
* enlève de l'ambiguïté et donc des items inutiles.
*/
public boolean canPredictLeftAdj() {
return (getPos() == DotPos.LEFT_ABOVE &&
// peut être le lieu d'une adjonction
// d'arbre auxiliaire gauche ou enveloppant
getDottedNode().isAdjAllowed() &&
(! getTree().isAuxiliary()));
}
public boolean canExploreUnderFoot() {
return (getPos() == DotPos.LEFT_ABOVE &&
getDottedNode().getType() == NodeType.FOOT_TYPE);
}
public boolean canPredictNoLeftAdjToComplete() {
return (halfAdjoinedAddr.isEmpty() &&
getPos() == DotPos.RIGHT_BELOW &&
// diffère avec canMoveToRightSubst
(! getDottedNode().isSubst()));
}
public boolean canResumeAdj() {
return ((! halfAdjoinedAddr.isEmpty()) &&
getPos() == DotPos.RIGHT_BELOW &&
// diffère avec canMoveToRightSubst
lastHalfAdjAddr().equals(getDot()));
}
public boolean canCompleteLeftAdj() {
return (raRoot() &&
// diffère d'un item acceptant
(getTree().getAuxType() == AuxType.LEFT_ADJTYPE));
}
public boolean canPredictRightAdj() {
return (getPos() == DotPos.RIGHT_ABOVE &&
getDottedNode().isAdjAllowed() &&
(! getTree().isAuxiliary()));
}
public boolean canCompleteRightAdj() {
return (raRoot() &&
// diffère d'un item acceptant
getTree().getAuxType() == AuxType.RIGHT_ADJTYPE);
}
/* Règles */
/*
* Règle appliquée sur un item tel que le point
* est en bas à gauche d'un noeud qui a un fils,
* elle ajoute un item avec le point en haut
* à gauche de ce fils.
*/
public Item moveDown() {
Item it = this.clone();
it.dot = dot.getSon(); // addr du 1er fils
it.pos = DotPos.LEFT_ABOVE;
return it;
}
public Item moveRight() {
Item it = this.clone();
it.dot = dot.getBrother(); // addr du 1er frère
it.pos = DotPos.LEFT_ABOVE;
return it;
}
public Item moveUp() {
Item it = this.clone();
it.dot = dot.getFather();
it.pos = DotPos.RIGHT_BELOW;
return it;
}
/*
* Règle appliquée sur un item dont le point
* est en bas à gauche d'un noeud de substitution.
*/
public Set<Item> predictSubstituable(Set<ElementaryTree> sourceTrees,
int currentStateNum) {
Set<Item> newItems = new HashSet<Item>();
for (ElementaryTree tree : sourceTrees)
if (tree.getRootCat().equals(getDottedNode().getCat()) &&
(! tree.isAuxiliary())) {
Item it =
new Item(tree, new GornAddress(),
DotPos.LEFT_ABOVE, currentStateNum);
it.addContextItem(this);
newItems.add(it);
}
return newItems;
}
/*
* Règle appliquée sur un item non acceptant dont
* le point est en haut à droite de la racine d'un
* arbre non auxiliaire. On remonte dans l'arbre
* où a eu lieu la substitution.
*/
public Set<Item> completeSubstitution() {
Set<Item> newItems = new HashSet<Item>();
for (Item parent : contextItems) { // un seul élément
Item newItem = parent.clone();
newItem.setPos(DotPos.RIGHT_ABOVE);
newItems.add(newItem);
//derivationTree.addToLeaf(it, newIt, DerivationType.SUBST);
// DerivationNode n = new DerivationNode(newIt, ...);
// chercher
// ga : it.getDot(), tree : it.getTree()
//
//derivationTree.pruneDescendantsOf();
}
return newItems;
}
/*
* Règle appliquée sur un item dont le point
* est en haut à gauche d'un noeud où peut se
* produire une adjonction, c-à-d. qui n'est
* pas un noeud de substitution ni un noeud
* lexical, et n'est pas d'un arbre auxiliaire.
*
* Quand cette règle est appliquée, moveDown
* doit aussi l'être sur le même item, elle
* représente le cas où aucune adjonction ne
* se produit.
*
* On cherche les arbres auxiliaires dont la
* racine est de même catégorie que le nœud
* où peut avoir lieu l'adjonction.
*/
public Set<Item> predictLeftAdjoinable(Set<ElementaryTree> sourceTrees,
int currentStateNum) {
Set<Item> newItems = new HashSet<Item>();
for (ElementaryTree tree : sourceTrees)
if (tree.getRootCat().equals(getDottedNode().getCat())
&& getTree().getAuxType() == AuxType.LEFT_ADJTYPE) {
Item adjoinable =
new Item(tree, new GornAddress(),
DotPos.LEFT_ABOVE, currentStateNum);
adjoinable.addContextItem(this);
newItems.add(adjoinable);
}
return newItems;
}
/*
* Règle appliquée sur un item dont le point
* est en haut à gauche d'un noeud pied (dans
* un arbre auxiliaire donc), lors d'une
* adjonction gauche ou enveloppante.
*
* On revient dans l'arbre pointé où a commencé
* l'adjonction de l'arbre auxiliaire, pour parcourir
* ce qui se trouve sous son pied (jusqu'à revenir
* au noeud pied pour repasser dans l'arbre adjoint).
*/
public Set<Item> exploreUnderFoot() {
Set<Item> newItems = new HashSet<Item>();
for (Item parent : contextItems) {
Item belowFoot = parent.clone();
// On conserve dans les piles l'item où il
// faudra revenir pour terminer l'adjonction
// en cours et à quelle addresse elle a lieu.
belowFoot.halfAdjoinedItems.add(this);
belowFoot.halfAdjoinedAddr.add(belowFoot.getDot());
// On ne modifie pas belowFoot.dot car on veut
// rester en haut à gauche.
newItems.add(belowFoot);
}
//TODO: peut-être le changer de place,
//mettre dans resumeAdj juste avant addContextItem
contextItems.clear();
return newItems;
}
/*
* Reprise de l'exploration de l'arbre auxiliaire
* gauche (ou enveloppant) en cours d'adjonction,
* plus précisément de sa partie droite.
*
* Règle appliquée quand on se trouve en bas à droite
* d'un noeud dont l'adresse est en haut de la pile
* halfAdjoinedAddr (indiquant qu'il reste au moins
* une adjonction à terminer à ce noeud).
*
* Tout comme exploreUnderFoot, une telle règle
* n'est pas nécessaire pour les adjonctions d'arbres
* auxiliaires droits, en raison du sens d'exploration
* des arbres (de gauche à droite).
*/
public Item resumeAdj() {
Item adjRightPart = lastHalfAdjItem().clone();
adjRightPart.setPos(DotPos.RIGHT_ABOVE);
// contextItems a été vidé au préalable
// dans exploreUnderFoot
adjRightPart.addContextItem(this);
return adjRightPart;
}
/*
* Règle appliquée quand le point est en haut à
* droite de la racine dans un arbre auxiliaire
* non accceptant.
*
* Deux cas sont possibles :
* - soit il reste des adjonctions gauches à compléter
* en ce noeud, auquel cas on reste en bas à droite du
* noeud (dans l'arbre en-dessous du pied). C'est ce que
* fait cette règle ;
* - soit il n'y en a plus à ce noeud, et on met le
* point en haut à droite (où peuvent se produire des
* adjonctions d'arbres auxiliaires droits).
*
* Dans tous les cas on supprime de la pile des
* adjonctions en cours celle que l'on vient de
* terminer.
*/
public Item completeLeftAdjunction() {
Item newItem = null;
for (Item context : contextItems) { // il n'y a qu'un élément
newItem = context.clone();
newItem.removeLastAdjRef();
}
return newItem;
}
/*
* Règle appliquée quand il n'y a plus
* d'adjonctions gauches à achever au
* noeud auquel on est (positionné
* en bas à droite).
*/
public Item predictNoLeftAdjToComplete() {
Item newItem = clone();
newItem.pos = DotPos.RIGHT_ABOVE;
return newItem;
}
/*
* Les deux règles suivantes suffisent pour
* les adjonctions droites. Pour remonter dans
* l'arbre sans tenter d'autres adjonctions,
* les méthodes moveRight et moveUp font
* l'affaire.
*/
public Set<Item> predictRightAdjoinable(Set<ElementaryTree> sourceTrees,
int currentStateNum) {
Set<Item> newItems = new HashSet<Item>();
for (ElementaryTree tree : sourceTrees)
if (tree.getRootCat().equals(getDottedNode().getCat())
&& tree.isAuxiliary()) {
Item adjoinable =
new Item(tree, tree.getFootAddr(),
DotPos.RIGHT_ABOVE, currentStateNum);
adjoinable.addContextItem(this);
newItems.add(adjoinable);
}
return newItems;
}
public Item completeRightAdjunction() {
Item newItem = null;
for (Item context : contextItems) // il n'y a qu'un élément
newItem = context.clone();
return newItem;
}
/*
* On ne peut remettre deux items égaux dans un
* même état. En définissant l'égalité pour les
* items, la méthode equals permet d'éviter
* certaines boucles infinies d'opérations sur
* les arbres ainsi que de ne pouvoir remonter
* dans le contexte de l'opération dans certains
* cas (comme les substitutions gauches en chaîne).
*/
public boolean equals(Item it) {
return (tree == it.tree &&
dot.equals(it.dot) &&
pos == it.pos &&
startState == it.startState &&
contextItems.equals(it.contextItems) &&
halfAdjoinedItems.equals(it.halfAdjoinedItems) &&
halfAdjoinedAddr.equals(it.halfAdjoinedAddr));
}
/*
* On utilise le même arbre et le même objet
* GornAddress, pas besoin d'en instancier des clones.
*/
public Item clone() {
return new Item(tree, dot, pos, startState,
contextItems, halfAdjoinedItems, halfAdjoinedAddr);
}
public Node getDottedNode() {
return tree.findNode(dot);
}
public boolean dotAtLexNode() {
return getDottedNode().isLex();
}
public String getWordAtDottedNode() {
if (this.dotAtLexNode())
return ((LexicalNode)getDottedNode()).getWord();
else
return null;
}
public void addContextItem(Item item) {
contextItems.add(item);
}
public GornAddress lastHalfAdjAddr() {
int lastIdx = halfAdjoinedAddr.size()-1;
return halfAdjoinedAddr.get(lastIdx);
}
public Item lastHalfAdjItem() {
int lastIdx = halfAdjoinedAddr.size()-1;
return halfAdjoinedItems.get(lastIdx);
}
public void removeLastAdjRef() {
int lastIdx = halfAdjoinedAddr.size()-1;
halfAdjoinedItems.remove(lastIdx);
halfAdjoinedAddr.remove(lastIdx);
}
public String toString() {
String res =
"[" + tree.getName() + ", "
+ dot.toString() + ", "
+ pos + ", "
+ startState + "]";
return res;
}
public void print() {
System.out.println(toString());
}
public ElementaryTree getTree() {
return tree;
}
public GornAddress getDot() {
return dot;
}
public DotPos getPos() {
return pos;
}
public int getStartState() {
return startState;
}
public void setTree(ElementaryTree newTree) {
tree = newTree;
}
public void setDot(GornAddress newDot) {
dot = newDot;
}
public void setPos(DotPos newPos) {
pos = newPos;
}
public void setStartState(int startState) {
this.startState = startState;
}
public void setContextItems(Set<Item> contextItems) {
this.contextItems = contextItems;
}
public Set<Item> getContextItems() {
return contextItems;
}
public void setHalfAdjoinedItems(List<Item> halfAdjoinedItems) {
this.halfAdjoinedItems = halfAdjoinedItems;
}
public List<Item> getHalfAdjoinedItems() {
return halfAdjoinedItems;
}
}//class