/*******************************************************************************
* Copyright (c) 2014, 2015 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.xtext.essentialocl.as2cs;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.ocl.pivot.Element;
import org.eclipse.ocl.pivot.utilities.ClassUtil;
import org.eclipse.ocl.xtext.base.as2cs.BaseLocationInFileProvider;
import org.eclipse.ocl.xtext.base.utilities.ElementUtil;
import org.eclipse.ocl.xtext.basecs.ElementCS;
import org.eclipse.ocl.xtext.essentialocl.attributes.NavigationUtil;
import org.eclipse.ocl.xtext.essentialoclcs.ExpCS;
import org.eclipse.ocl.xtext.essentialoclcs.InfixExpCS;
import org.eclipse.ocl.xtext.essentialoclcs.NameExpCS;
import org.eclipse.ocl.xtext.essentialoclcs.OperatorExpCS;
import org.eclipse.xtext.util.ITextRegion;
import org.eclipse.xtext.util.TextRegionWithLineInformation;
/**
* EssentialOCLLocationInFileProvider ensure that the full text regions for OperatorCSes cover the full source and argument range.
*/
public class EssentialOCLLocationInFileProvider extends BaseLocationInFileProvider
{
private static final @SuppressWarnings("null")@NonNull ITextRegion EMPTY_REGION = ITextRegion.EMPTY_REGION;
@Override
protected @NonNull ITextRegion getTextRegion(EObject obj, boolean isSignificant) {
ITextRegion textRegion;
ElementCS csModelElement = null;
if (obj instanceof Element) {
csModelElement = ElementUtil.getCsElement((Element) obj);
}
else if (obj instanceof ElementCS) {
csModelElement = (ElementCS)obj;
}
if (csModelElement instanceof ExpCS) {
textRegion = getTextRegionCS((ExpCS)csModelElement, isSignificant);
}
else if (csModelElement == null) {
textRegion = getTextRegionNoCS(obj, isSignificant);
}
else {
textRegion = super.getTextRegion(obj, isSignificant);
}
/* StringBuilder s = new StringBuilder();
s.append(obj.eClass().getName());
s.append(" ");
ElementUtil.appendTextRegion(s, textRegion, isSignificant);
if (csModelElement != null) {
String ss = csModelElement.toString().replace("\n", "\\n");
if (ss.length() > 100) {
ss = ss.substring(0, 100) + "...";
}
s.append(ss);
}
System.out.println(s.toString()); */
return textRegion;
}
/**
* For a CS-less element, the full text region is expanded to cover all children.
* <p>
* This ensures that for an infix "or" the full region covers the input terms.
*/
public @NonNull ITextRegion getTextRegionCS(@NonNull ExpCS csExp, boolean isSignificant) {
if (!isSignificant) {
ExpCS csLeftmost = csExp;
ExpCS csRightmost = csExp;
if (csLeftmost instanceof NameExpCS) {
EObject csLeft = ((NameExpCS) csLeftmost).getLocalLeft();
if (NavigationUtil.isNavigationInfixExp(csLeft)) {
assert csLeft != null;
csLeftmost = (InfixExpCS)csLeft;
}
}
while (csLeftmost instanceof OperatorExpCS) {
ExpCS csSource = ((OperatorExpCS)csLeftmost).getSource();
if (csSource != null) {
csLeftmost = csSource;
}
else {
break;
}
}
while (csRightmost instanceof InfixExpCS) {
ExpCS csArgument = ((InfixExpCS)csRightmost).getArgument();
if (csArgument != null) {
csRightmost = csArgument;
}
else {
break;
}
}
ITextRegion leftRegion = super.getTextRegion(csLeftmost, isSignificant);
ITextRegion rightRegion = super.getTextRegion(csRightmost, isSignificant);
return ClassUtil.nonNullState(leftRegion.merge(rightRegion));
}
else {
return super.getTextRegion(csExp, isSignificant);
}
}
/**
* For a CS-less element, the text region is determined from the CS parent and child. The
* significant region is less between the child full region and the parent signifucant region.
* The full region additionally covers the child.
* <p>
* This ensures that for an implicit oclAsSet() the significant region is the navigation operator.
*/
protected @NonNull ITextRegion getTextRegionNoCS(EObject obj, boolean isSignificant) {
Element asParentElement = null;
for (EObject eObject = obj; eObject != null; eObject = eObject.eContainer()) {
if (eObject instanceof Element) {
ElementCS csModelElement = ElementUtil.getCsElement((Element) eObject);
if (csModelElement != null) {
asParentElement = (Element)eObject;
break;
}
}
}
if (asParentElement == null) {
return EMPTY_REGION;
}
ITextRegion parentSignificantTextRegion = getTextRegion(asParentElement, true);
ITextRegion childrenFullTextRegion = null;
for (EObject eChild : obj.eContents()) {
if (eChild instanceof Element) {
ElementCS csModelElement = ElementUtil.getCsElement((Element) eChild);
if (csModelElement != null) {
ITextRegion childFullTextRegion = getTextRegion(csModelElement, false);
childrenFullTextRegion = childrenFullTextRegion != null ? childrenFullTextRegion.merge(childFullTextRegion) : childFullTextRegion;
}
}
}
if (childrenFullTextRegion == null) {
return new TextRegionWithLineInformation(parentSignificantTextRegion.getOffset(), 0, 0, 0);
}
int parentStart = parentSignificantTextRegion.getOffset();
int parentEnd = parentStart + parentSignificantTextRegion.getLength();
int childStart = childrenFullTextRegion.getOffset();
int childEnd = childStart + childrenFullTextRegion.getLength();
int fullStart = childStart;
int fullEnd = childEnd;
int significantStart;
int significantEnd;
if (parentEnd < fullStart) {
significantStart = parentEnd;
significantEnd = fullStart;
fullStart = parentEnd;
}
else if (fullEnd < parentStart) {
significantStart = fullEnd;
significantEnd = parentStart;
fullEnd = parentStart;
}
else {
significantStart = childEnd;
significantEnd = childEnd;
}
if (isSignificant) {
return new TextRegionWithLineInformation(significantStart, significantEnd - significantStart, 0, 0);
}
else {
return new TextRegionWithLineInformation(fullStart, fullEnd - fullStart, 0, 0);
}
}
}