protected String findPrefix( List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> expectedElements, org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal expectedAtCursor, String content, int cursorOffset) { if (cursorOffset < 0) { return ""; } int end = 0; for (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal expectedElement : expectedElements) { if (expectedElement == expectedAtCursor) { final int start = expectedElement.getStartExcludingHiddenTokens(); if (start >= 0 && start < Integer.MAX_VALUE) { end = start; } break; } } end = Math.min(end, cursorOffset); final String prefix = content.substring(end, Math.min(content.length(), cursorOffset)); return prefix; }
/** * Calculates the prefix for each given expected element. The prefix depends on the current * document content, the cursor position, and the position where the element is expected. */ protected void setPrefixes( List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> expectedElements, String content, int cursorOffset) { if (cursorOffset < 0) { return; } for (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal expectedElement : expectedElements) { String prefix = findPrefix(expectedElements, expectedElement, content, cursorOffset); expectedElement.setPrefix(prefix); } }
public org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal[] getElementsExpectedAt( org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal[] allExpectedElements, int cursorOffset) { List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> expectedAtCursor = new ArrayList<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal>(); for (int i = 0; i < allExpectedElements.length; i++) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal expectedElement = allExpectedElements[i]; int startIncludingHidden = expectedElement.getStartIncludingHiddenTokens(); int end = getEnd(allExpectedElements, i); if (cursorOffset >= startIncludingHidden && cursorOffset <= end) { expectedAtCursor.add(expectedElement); } } return expectedAtCursor.toArray( new org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal [expectedAtCursor.size()]); }
/** * Removes all expected elements that refer to the same terminal and that start at the same * position. */ protected void removeDuplicateEntries( List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> expectedElements) { int size = expectedElements.size(); // We split the list of expected elements into buckets where each bucket contains // the elements that start at the same position. Map<Integer, List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal>> map = new LinkedHashMap< Integer, List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal>>(); for (int i = 0; i < size; i++) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal elementAtIndex = expectedElements.get(i); int start1 = elementAtIndex.getStartExcludingHiddenTokens(); List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> list = map.get(start1); if (list == null) { list = new ArrayList< org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal>(); map.put(start1, list); } list.add(elementAtIndex); } // Then, we remove all duplicate elements from each bucket individually. for (int position : map.keySet()) { List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> list = map.get(position); removeDuplicateEntriesFromBucket(list); } // After removing all duplicates, we merge the buckets. expectedElements.clear(); for (int position : map.keySet()) { List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> list = map.get(position); expectedElements.addAll(list); } }
/** * Removes all expected elements that refer to the same terminal. Attention: This method assumes * that the given list of expected terminals contains only elements that start at the same * position. */ protected void removeDuplicateEntriesFromBucket( List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> expectedElements) { int size = expectedElements.size(); for (int i = 0; i < size - 1; i++) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal elementAtIndex = expectedElements.get(i); org.emftext.language.xpath3.resource.xpath3.IXpath3ExpectedElement terminal = elementAtIndex.getTerminal(); for (int j = i + 1; j < size; ) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal elementAtNext = expectedElements.get(j); EClass metaClass = elementAtIndex.getContainmentTrace().getStartClass(); EClass nextMetaClass = elementAtNext.getContainmentTrace().getStartClass(); org.emftext.language.xpath3.resource.xpath3.IXpath3ExpectedElement nextTerminal = elementAtNext.getTerminal(); // Terminals that have a different root meta class in the containment trace must // be kept because they can the decision whether an expected terminals is valid or // not depends on the root of the containment trace. boolean differentMetaclass = metaClass != nextMetaClass; if (terminal.equals(nextTerminal) && !differentMetaclass) { expectedElements.remove(j); size--; } else { j++; } } } }
/** Removes all proposals for keywords that end before the given index. */ protected void removeKeywordsEndingBeforeIndex( Collection<org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal> proposals, int index) { List<org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal> toRemove = new ArrayList<org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal>(); for (org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal proposal : proposals) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal expectedTerminal = proposal.getExpectedTerminal(); org.emftext.language.xpath3.resource.xpath3.IXpath3ExpectedElement terminal = expectedTerminal.getTerminal(); if (terminal instanceof org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedCsString) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedCsString csString = (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedCsString) terminal; int startExcludingHiddenTokens = expectedTerminal.getStartExcludingHiddenTokens(); if (startExcludingHiddenTokens + csString.getValue().length() - 1 < index) { toRemove.add(proposal); } } } proposals.removeAll(toRemove); }
/** * Calculates the end index of the expected element at allExpectedElements[index]. To determine * the end, the subsequent expected elements from the array of all expected elements are used. An * element is considered to end one character before the next elements starts. */ protected int getEnd( org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal[] allExpectedElements, int indexInList) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal elementAtIndex = allExpectedElements[indexInList]; int startIncludingHidden = elementAtIndex.getStartIncludingHiddenTokens(); int startExcludingHidden = elementAtIndex.getStartExcludingHiddenTokens(); for (int i = indexInList + 1; i < allExpectedElements.length; i++) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal elementAtI = allExpectedElements[i]; int startIncludingHiddenForI = elementAtI.getStartIncludingHiddenTokens(); int startExcludingHiddenForI = elementAtI.getStartExcludingHiddenTokens(); if (startIncludingHidden != startIncludingHiddenForI || startExcludingHidden != startExcludingHiddenForI) { return startIncludingHiddenForI - 1; } } return Integer.MAX_VALUE; }
protected EObject findCorrectContainer( org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal expectedTerminal) { EObject container = expectedTerminal.getContainer(); EClass ruleMetaclass = expectedTerminal.getTerminal().getRuleMetaclass(); if (ruleMetaclass.isInstance(container)) { // container is correct for expected terminal return container; } // the container is wrong EObject parent = null; EObject previousParent = null; EObject correctContainer = null; EObject hookableParent = null; org.emftext.language.xpath3.resource.xpath3.grammar.Xpath3ContainmentTrace containmentTrace = expectedTerminal.getContainmentTrace(); EClass startClass = containmentTrace.getStartClass(); org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ContainedFeature currentLink = null; org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ContainedFeature previousLink = null; org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ContainedFeature[] containedFeatures = containmentTrace.getPath(); for (int i = 0; i < containedFeatures.length; i++) { currentLink = containedFeatures[i]; if (i > 0) { previousLink = containedFeatures[i - 1]; } EClass containerClass = currentLink.getContainerClass(); hookableParent = findHookParent(container, startClass, currentLink, parent); if (hookableParent != null) { // we found the correct parent break; } else { previousParent = parent; parent = containerClass.getEPackage().getEFactoryInstance().create(containerClass); if (parent != null) { if (previousParent == null) { // replace container for expectedTerminal with correctContainer correctContainer = parent; } else { // This assignment is only performed to get rid of a warning about a potential // null pointer access. Variable 'previousLink' cannot be null here, because it is // initialized for all loop iterations where 'i' is greather than 0 and for the // case where 'i' equals zero, this path is never executed, because // 'previousParent' is null in this case. org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ContainedFeature link = previousLink; org.emftext.language.xpath3.resource.xpath3.util.Xpath3EObjectUtil.setFeature( parent, link.getFeature(), previousParent, false); } } } } if (correctContainer == null) { correctContainer = container; } if (currentLink == null) { return correctContainer; } hookableParent = findHookParent(container, startClass, currentLink, parent); final EObject finalHookableParent = hookableParent; final EStructuralFeature finalFeature = currentLink.getFeature(); final EObject finalParent = parent; if (parent != null && hookableParent != null) { expectedTerminal.setAttachmentCode( new Runnable() { public void run() { org.emftext.language.xpath3.resource.xpath3.util.Xpath3EObjectUtil.setFeature( finalHookableParent, finalFeature, finalParent, false); } }); } return correctContainer; }
protected Collection<org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal> deriveProposals( final org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal expectedTerminal, String content, org.emftext.language.xpath3.resource.xpath3.IXpath3TextResource resource, int cursorOffset) { org.emftext.language.xpath3.resource.xpath3.IXpath3ExpectedElement expectedElement = (org.emftext.language.xpath3.resource.xpath3.IXpath3ExpectedElement) expectedTerminal.getTerminal(); if (expectedElement instanceof org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedCsString) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedCsString csString = (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedCsString) expectedElement; return handleKeyword( expectedTerminal, csString, expectedTerminal.getPrefix(), expectedTerminal.getContainer()); } else if (expectedElement instanceof org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedBooleanTerminal) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedBooleanTerminal expectedBooleanTerminal = (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedBooleanTerminal) expectedElement; return handleBooleanTerminal( expectedTerminal, expectedBooleanTerminal, expectedTerminal.getPrefix()); } else if (expectedElement instanceof org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedEnumerationTerminal) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedEnumerationTerminal expectedEnumerationTerminal = (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedEnumerationTerminal) expectedElement; return handleEnumerationTerminal( expectedTerminal, expectedEnumerationTerminal, expectedTerminal.getPrefix()); } else if (expectedElement instanceof org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedStructuralFeature) { final org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedStructuralFeature expectedFeature = (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedStructuralFeature) expectedElement; final EStructuralFeature feature = expectedFeature.getFeature(); final EClassifier featureType = feature.getEType(); final EObject container = findCorrectContainer(expectedTerminal); // Here it gets really crazy. We need to modify the model in a way that reflects // the state the model would be in, if the expected terminal were present. After // computing the corresponding completion proposals, the original state of the // model is restored. This procedure is required, because different models can be // required for different completion situations. This can be particularly observed // when the user has not yet typed a character that starts an element to be // completed. final Collection<org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal> proposals = new ArrayList< org.emftext.language.xpath3.resource.xpath3.ui.Xpath3CompletionProposal>(); expectedTerminal.materialize( new Runnable() { public void run() { if (feature instanceof EReference) { EReference reference = (EReference) feature; if (featureType instanceof EClass) { if (reference.isContainment()) { // the FOLLOW set should contain only non-containment references assert false; } else { proposals.addAll( handleNCReference( expectedTerminal, container, reference, expectedTerminal.getPrefix(), expectedFeature.getTokenName())); } } } else if (feature instanceof EAttribute) { EAttribute attribute = (EAttribute) feature; if (featureType instanceof EEnum) { EEnum enumType = (EEnum) featureType; proposals.addAll( handleEnumAttribute( expectedTerminal, expectedFeature, enumType, expectedTerminal.getPrefix(), container)); } else { // handle EAttributes (derive default value depending on the type of the // attribute, figure out token resolver, and call deResolve()) proposals.addAll( handleAttribute( expectedTerminal, expectedFeature, container, attribute, expectedTerminal.getPrefix())); } } else { // there should be no other subclass of EStructuralFeature assert false; } } }); // Return the proposals that were computed in the closure call. return proposals; } else { // there should be no other class implementing IExpectedElement assert false; } return Collections.emptyList(); }
protected void removeInvalidEntriesAtEnd( List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> expectedElements) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3FollowSetGroupList followSetGroupList = new org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3FollowSetGroupList( expectedElements); List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3FollowSetGroup> followSetGroups = followSetGroupList.getFollowSetGroups(); int lastStartExcludingHiddenTokens = -1; for (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3FollowSetGroup followSetGroup : followSetGroups) { boolean sameStartExcludingHiddenTokens = followSetGroup.hasSameStartExcludingHiddenTokens(lastStartExcludingHiddenTokens); lastStartExcludingHiddenTokens = followSetGroup.getStartExcludingHiddenTokens(); EObject container = followSetGroup.getContainer(); EClass currentRule = null; if (container != null) { currentRule = container.eClass(); } List<org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal> expectedTerminals = followSetGroup.getExpectedTerminals(); for (org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal expectedTerminal : expectedTerminals) { org.emftext.language.xpath3.resource.xpath3.IXpath3ExpectedElement terminalAtIndex = expectedTerminal.getTerminal(); EClass ruleMetaclass = terminalAtIndex.getRuleMetaclass(); boolean differentRule = currentRule != ruleMetaclass; // If the two expected elements have a different parent in the syntax definition, // we must not discard the second element, because it probably stems from a parent // rule. org.emftext.language.xpath3.resource.xpath3.grammar.Xpath3ContainmentTrace containmentTrace = expectedTerminal.getContainmentTrace(); boolean fitsAtCurrentPosition = fitsAtCurrentPosition(container, containmentTrace); boolean inContainmentTrace = pathToRootContains(container, expectedTerminal.getTerminal().getRuleMetaclass()); boolean keepElement = true; if (differentRule && !inContainmentTrace) { if (!fitsAtCurrentPosition) { keepElement = false; } } if (sameStartExcludingHiddenTokens) { keepElement = false; } if (keepElement) { } else { // We must not call expectedElements.remove(expectedTerminal) because the // hashCode() method of ExpectedTerminal does not consider the start positions and // remove the wrong elements. for (int i = 0; i < expectedElements.size(); i++) { org.emftext.language.xpath3.resource.xpath3.mopp.Xpath3ExpectedTerminal next = expectedElements.get(i); if (next == expectedTerminal) { expectedElements.remove(i); break; } } } } } }