/*=============================================================================# # Copyright (c) 2009-2016 Stephan Wahlbrink (WalWare.de) 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: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.docmlet.tex.internal.core.model; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.jface.text.IRegion; import de.walware.ecommons.ltk.IElementName; import de.walware.ecommons.ltk.ast.IAstNode; import de.walware.ecommons.ltk.core.model.IEmbeddedForeignElement; import de.walware.ecommons.ltk.core.model.IModelElement; import de.walware.ecommons.ltk.core.model.ISourceStructElement; import de.walware.ecommons.ltk.core.model.ISourceUnit; import de.walware.ecommons.ltk.core.model.ISourceUnitModelInfo; import de.walware.docmlet.tex.core.ast.Embedded; import de.walware.docmlet.tex.core.ast.TexAstNode; import de.walware.docmlet.tex.core.model.ITexSourceElement; import de.walware.docmlet.tex.core.model.TexElementName; import de.walware.docmlet.tex.core.model.TexModel; public abstract class LtxSourceElement implements ITexSourceElement, IRegion { private static final List<LtxSourceElement> NO_TEXSOURCE_CHILDREN = Collections.emptyList(); static final List<? extends ISourceStructElement> getChildren(final List<? extends ISourceStructElement> children, final IModelElement.Filter filter) { if (filter == null) { return children; } else { final ArrayList<ISourceStructElement> filtered = new ArrayList<>(children.size()); for (final ISourceStructElement child : children) { if (filter.include(child)) { filtered.add(child); } } return filtered; } } static final boolean hasChildren(final List<? extends ISourceStructElement> children, final IModelElement.Filter filter) { if (filter == null) { return (!children.isEmpty()); } else { for (final ISourceStructElement child : children) { if (filter.include(child)) { return true; } } return false; } } public abstract static class Container extends LtxSourceElement { List<LtxSourceElement> fChildren = NO_TEXSOURCE_CHILDREN; IRegion nameRegion; private final TexAstNode fAstNode; public Container(final int type, final TexAstNode astNode) { super(type); fAstNode = astNode; } @Override public IRegion getNameSourceRange() { return nameRegion; } @Override public boolean hasSourceChildren(final Filter filter) { return hasChildren(fChildren, filter); } @Override public List<? extends ISourceStructElement> getSourceChildren(final Filter filter) { return getChildren(fChildren, filter); } @Override public abstract Container getModelParent(); @Override public boolean hasModelChildren(final Filter filter) { return hasChildren(fChildren, filter); } @Override public List<? extends IModelElement> getModelChildren(final Filter filter) { return getChildren(fChildren, filter); } @Override public Object getAdapter(final Class required) { if (IAstNode.class.equals(required)) { return fAstNode; } return super.getAdapter(required); } } public static class SourceContainer extends Container { private final ISourceUnit fSourceUnit; public SourceContainer(final int type, final ISourceUnit su, final TexAstNode astNode) { super(type, astNode); fSourceUnit = su; } @Override public String getId() { return fSourceUnit.getId(); } @Override public TexElementName getElementName() { final IElementName elementName = fSourceUnit.getElementName(); if (elementName instanceof TexElementName) { return (TexElementName) elementName; } return TexElementName.create(TexElementName.RESOURCE, elementName.getSegmentName()); } @Override public ISourceUnit getSourceUnit() { return fSourceUnit; } @Override public boolean exists() { final ISourceUnitModelInfo modelInfo = getSourceUnit().getModelInfo(TexModel.LTX_TYPE_ID, 0, null); return (modelInfo != null && modelInfo.getSourceElement() == this); } @Override public boolean isReadOnly() { return fSourceUnit.isReadOnly(); } @Override public ISourceStructElement getSourceParent() { return null; } @Override public Container getModelParent() { return null; } } public static class StructContainer extends Container { private final Container fParent; public StructContainer(final int type, final Container parent, final TexAstNode astNode) { super(type, astNode); fParent = parent; fOffset = astNode.getOffset(); fLength = astNode.getLength(); } @Override public ISourceUnit getSourceUnit() { return fParent.getSourceUnit(); } @Override public boolean exists() { return fParent.exists(); } @Override public boolean isReadOnly() { return fParent.isReadOnly(); } @Override public ISourceStructElement getSourceParent() { return fParent; } @Override public Container getModelParent() { return fParent; } } public static class EmbeddedRef extends LtxSourceElement implements IEmbeddedForeignElement { private final Container fParent; private final String fExternType; private ISourceStructElement fForeign; private final Embedded fAstNode; protected EmbeddedRef(final String externType, final Container parent, final Embedded astNode) { super(IModelElement.C1_EMBEDDED); fExternType = externType; fParent = parent; fAstNode = astNode; } @Override public String getId() { final String name= getElementName().getDisplayName(); final StringBuilder sb= new StringBuilder(name.length() + 32); sb.append(Integer.toHexString(getElementType() & MASK_C2)); sb.append("<noweb:"); sb.append(fExternType); sb.append(">:"); sb.append(name); sb.append('#'); sb.append(this.fOccurrenceCount); return sb.toString(); } @Override public IElementName getElementName() { return (fForeign != null) ? fForeign.getElementName() : TexElementName.create(0, ""); } @Override public IRegion getNameSourceRange() { return (fForeign != null) ? fForeign.getNameSourceRange() : null; } @Override public ISourceUnit getSourceUnit() { return fParent.getSourceUnit(); } @Override public boolean exists() { return fParent.exists(); } @Override public boolean isReadOnly() { return fParent.isReadOnly(); } @Override public ITexSourceElement getModelParent() { return fParent; } @Override public boolean hasModelChildren(final Filter filter) { return false; } @Override public List<? extends IModelElement> getModelChildren(final Filter filter) { return null; } @Override public ISourceStructElement getForeignElement() { return fForeign; } @Override public ISourceStructElement getSourceParent() { return fParent; } @Override public boolean hasSourceChildren(final Filter filter) { return (fForeign != null && (filter == null || filter.include(fForeign))); } @Override public List<? extends ISourceStructElement> getSourceChildren(final Filter filter) { return (fForeign != null && (filter == null || filter.include(fForeign))) ? Collections.singletonList(fForeign) : NO_TEXSOURCE_CHILDREN; } public void setForeign(final ISourceStructElement foreign) { fForeign = foreign; } @Override public Object getAdapter(final Class required) { if (IAstNode.class.equals(required)) { return fAstNode; } { final Object adapter = super.getAdapter(required); if (adapter != null) { return adapter; } } return fForeign.getAdapter(required); } @Override public int hashCode() { int h = (IModelElement.C1_EMBEDDED & MASK_C2) * fExternType.hashCode() + fOccurrenceCount; if (fForeign != null) { h =+ fForeign.hashCode() * 23917; } return h; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof EmbeddedRef)) { return false; } final EmbeddedRef other = (EmbeddedRef) obj; return ((getSourceParent().equals(other.getSourceParent()) ) && ((fForeign != null) ? fForeign.equals(other.fForeign) : null == other.fForeign) ); } } private final int fType; protected TexElementName fName; protected int fOccurrenceCount; protected int fOffset; protected int fLength; protected LtxSourceElement(final int type) { fType = type; } @Override public final String getModelTypeId() { return TexModel.LTX_TYPE_ID; } @Override public final int getElementType() { return fType; } @Override public String getId() { final String name= getElementName().getDisplayName(); final StringBuilder sb= new StringBuilder(name.length() + 16); sb.append(Integer.toHexString(this.fType & MASK_C2)); sb.append(':'); sb.append(name); sb.append('#'); sb.append(this.fOccurrenceCount); return sb.toString(); } @Override public IElementName getElementName() { return fName; } @Override public IRegion getSourceRange() { return this; } @Override public int getOffset() { return fOffset; } @Override public int getLength() { return fLength; } @Override public IRegion getDocumentationRange() { return null; } @Override public Object getAdapter(final Class adapter) { return null; } @Override public int hashCode() { return (fType & MASK_C2) * getElementName().hashCode() + fOccurrenceCount; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof LtxSourceElement)) { return false; } final LtxSourceElement other = (LtxSourceElement) obj; return ( (fType & MASK_C2) == (other.fType & MASK_C2)) && (fOccurrenceCount == other.fOccurrenceCount) && ( ((fType & MASK_C1) == C1_SOURCE) || (getSourceParent().equals(other.getSourceParent())) ) && (getElementName().equals(other.getElementName()) ); } }