package com.openMap1.mapper.query; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import java.util.Vector; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import com.openMap1.mapper.util.ModelUtil; public class QueryParserImpl_Ecore extends QueryParserImpl_Super implements QueryParser{ private EPackage classModel; //------------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------------ /** * * @param m * @param code * @param errors * @param tracing */ public QueryParserImpl_Ecore (EPackage classModel, String code,Vector<String[]> errors, boolean tracing) { super(errors, tracing); this.code = code; this.classModel = classModel; } //------------------------------------------------------------------------------------ // Parsing methods which depend on the Ecore model //------------------------------------------------------------------------------------ /** check that a field for writing out in the query is of the form 'class.property', where 'class' is an unqualified name of a mapped class and 'property' is one of its properties, direct or inherited. If so, return a three-element vector of (qualified class name,property,'present'); If the property is not represented in the Ecore model, return (class,property,'absent') (or null for now); otherwise (if no full stop or class not represented in the Ecore model) return null; The field may also be defined by some chain of associations from a start class. In this case, note the associations and return the Vector (qualified class name,property,'present') for the last class in the chain. if isCore = true, set the core status of all classes involved. */ Vector<WriteField> makeWriteFields(String fName, boolean isCore) { Vector<WriteField> res= new Vector<WriteField>(); // Vector of subfields separated by '.' Vector<String> subFields = stopSeparated(fName); // input field with no '.' is illegal before 'where' if (subFields.size() < 2) {errorMessage("Field for query display should be of the form 'class.property', " + " or 'class.role...property', but '" + fName + "' has no internal full stop.");} // get all candidates for the first class in the chain (packages are not defined in queries) String bareClassName = subFields.elementAt(0); List<EClass> classes = ModelUtil.getAllNamedClasses(classModel, bareClassName); if (classes.size() == 0) errorMessage("There is no class '" + bareClassName + "' in the class model"); // the last field is always the property to be shown String inPropertyName = subFields.get(subFields.size() - 1); int links = subFields.size() - 2; // number of link associations - may be 0 boolean foundALinkChain = (links == 0); // starts true if there are no links to find // try out all candidate start classes (there will usually be one) for (Iterator<EClass> it = classes.iterator();it.hasNext();) { EClass startClass = it.next(); EClass nextClass = startClass; // will end up as the final class which has the property boolean foundLinks = true; // remains true if there are no links to find String assocChain = ""; QueryClass sourceQueryClass = makeOrFindQueryClass(startClass,assocChain); // note this is guaranteed only for 'longhand' queries, where all write filed start with the start class sourceQueryClass.setCore(); // follow the association chain as far as you can for (int link = 0; link < links; link++) if (foundLinks) { // allow for old notation '(ref)class' as well as 'ref' String refName = getRefName(subFields.get(link + 1)); assocChain = assocChain + "." + refName; // follow one link - this does not allow for inherited associations EStructuralFeature next = nextClass.getEStructuralFeature(refName); // success if ((next != null) && (next instanceof EReference)) { EReference nextRef = (EReference)next; nextClass = (EClass)nextRef.getEType(); QueryClass nextQueryClass = makeOrFindQueryClass(nextClass,assocChain); if (isCore) nextQueryClass.setCore(); noteNewLinkAssociation(sourceQueryClass,nextRef,nextQueryClass); sourceQueryClass = nextQueryClass; //ready for next link in the chain } // failure at this link else foundLinks = false; } if (foundLinks) foundALinkChain = true; // pick up the first class (with the start class name) which has both the association chain and the final property if ((foundLinks) && (res.size() == 0)) { // '*' stands for all properties of the class (including inherited properties) if (inPropertyName.equals("*")) { for (Iterator<EStructuralFeature> iu = sourceQueryClass.getEClass().getEAllStructuralFeatures().iterator();iu.hasNext();) { EStructuralFeature feat = iu.next(); if (feat instanceof EAttribute) { String propName = feat.getName(); res.add(new WriteField(sourceQueryClass,propName,this)); } } } // '**' stands for all properties of the class or any class got by a containment relation else if (inPropertyName.equals("**")) { addAllNestedProperties(res,sourceQueryClass,assocChain); } // anything other than '*' or '**' is a single property else { EStructuralFeature feat = sourceQueryClass.getEClass().getEStructuralFeature(inPropertyName); if ((feat != null) && (feat instanceof EAttribute)) res.add(new WriteField(sourceQueryClass,inPropertyName,this)); // otherwise res.size() == 0 triggers an error message } } } // detect error conditions, which no choice of start class solves if (!foundALinkChain) errorMessage("Cannot follow the chain of associations in '" + fName + "'"); else if (res.size() == 0) errorMessage("Cannot find any final property '" + inPropertyName + "' in '" + fName + "'"); return res; } /** * add to the list of writeFields all properties of this class , * and any class got from it by a containment relation. * For those classes, add the queryClass and the link association if necessary * @param res * @param sourceQueryClass */ private void addAllNestedProperties(Vector<WriteField>res, QueryClass sourceQueryClass,String assocChain) { for (Iterator<EStructuralFeature> iu = sourceQueryClass.getEClass().getEAllStructuralFeatures().iterator();iu.hasNext();) { EStructuralFeature feat = iu.next(); if (feat instanceof EAttribute) { String propName = feat.getName(); res.add(new WriteField(sourceQueryClass,propName,this)); } else if (feat instanceof EReference) { EReference ref = (EReference)feat; if (ref.isContainment()) { String newAssocChain = assocChain + "." + ref.getName(); EClass nextClass = (EClass)ref.getEType(); // note the next QueryClass and the link to it QueryClass nextQueryClass = makeOrFindQueryClass(nextClass,newAssocChain); noteNewLinkAssociation(sourceQueryClass,ref,nextQueryClass); // recursive step to add all WriteFields for the new class etc. addAllNestedProperties(res,nextQueryClass,newAssocChain); } } } } /** * make a new query class and return it, unless a matching query class can be found in the existing set; * in that case return the matching query class * @param baseClass * @param assocChain * @return */ private QueryClass makeOrFindQueryClass(EClass baseClass, String assocChain) { QueryClass newClass = new QueryClass(baseClass,assocChain,this); QueryClass matchingClass = newClass.matchInList(queryClasses); if (matchingClass != null) newClass = matchingClass; else queryClasses.add(newClass); return newClass; } /** * if propName is a valid property of the class, note that it is to be output; * otherwise return null * @param theClass * @param propName * @return */ private Vector<String> propertyVect(EClass theClass, String propName) { Vector<String> res = null; // this does not allow for inherited properties EStructuralFeature feat = theClass.getEStructuralFeature(propName); if ((feat != null) && (feat instanceof EAttribute)) { res = new Vector<String>(); res.addElement(ModelUtil.getQualifiedClassName(theClass)); res.addElement(propName); res.addElement("present"); } return res; } /** * * @param sourceQueryClass * @param nextRef * @param nextQueryClass */ private void noteNewLinkAssociation(QueryClass sourceQueryClass, EReference nextRef,QueryClass nextQueryClass) { LinkAssociation link = new LinkAssociation(sourceQueryClass,nextRef,nextQueryClass,this); linkAssociations.put(link.key(), link); } /** * for a link name which is either 'refName' or '(refName)className' * return only the refName * (so there is no check on className) * @param linkName * @return */ private String getRefName(String linkName) { StringTokenizer linkParts = new StringTokenizer(linkName,"()"); return linkParts.nextToken(); } /** * this is only called when some 'where' conditions are of the form 'Class.relation.class'. * This occurs only rarely, as link associations are usually implied by write fields; * so it has not yet been implemented */ int handleLinkAssociation(String w1, String w2, String w3, Vector<String> after, int pos) { // TODO Auto-generated method stub return 0; } }