/*=============================================================================# # 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.wikitext.internal.core.model; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.text.IRegion; import de.walware.jcommons.collections.ImCollections; import de.walware.jcommons.collections.ImList; 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.wikitext.core.ast.Embedded; import de.walware.docmlet.wikitext.core.ast.WikitextAstNode; import de.walware.docmlet.wikitext.core.model.IWikitextSourceElement; import de.walware.docmlet.wikitext.core.model.WikitextElementName; import de.walware.docmlet.wikitext.core.model.WikitextModel; public abstract class WikitextSourceElement implements IWikitextSourceElement, IRegion { private static final ImList<WikitextSourceElement> NO_CHILDREN= ImCollections.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 WikitextSourceElement { List<WikitextSourceElement> children= NO_CHILDREN; IRegion nameRegion; private final WikitextAstNode astNode; public Container(final int type, final WikitextAstNode astNode) { super(type); this.astNode= astNode; } @Override public IRegion getNameSourceRange() { return this.nameRegion; } @Override public boolean hasSourceChildren(final Filter filter) { return hasChildren(this.children, filter); } @Override public List<? extends ISourceStructElement> getSourceChildren(final Filter filter) { return getChildren(this.children, filter); } @Override public abstract Container getModelParent(); @Override public boolean hasModelChildren(final Filter filter) { return hasChildren(this.children, filter); } @Override public List<? extends IModelElement> getModelChildren(final Filter filter) { return getChildren(this.children, filter); } @Override public Object getAdapter(final Class required) { if (IAstNode.class.equals(required)) { return this.astNode; } return super.getAdapter(required); } } public static class SourceContainer extends Container { private final ISourceUnit sourceUnit; public SourceContainer(final int type, final ISourceUnit su, final WikitextAstNode astNode) { super(type, astNode); this.sourceUnit= su; } @Override public String getId() { return this.sourceUnit.getId(); } @Override public WikitextElementName getElementName() { final IElementName elementName= this.sourceUnit.getElementName(); if (elementName instanceof WikitextElementName) { return (WikitextElementName) elementName; } return WikitextElementName.create(WikitextElementName.RESOURCE, elementName.getSegmentName()); } @Override public ISourceUnit getSourceUnit() { return this.sourceUnit; } @Override public boolean exists() { final ISourceUnitModelInfo modelInfo= getSourceUnit().getModelInfo(WikitextModel.WIKIDOC_TYPE_ID, 0, null); return (modelInfo != null && modelInfo.getSourceElement() == this); } @Override public boolean isReadOnly() { return this.sourceUnit.isReadOnly(); } @Override public ISourceStructElement getSourceParent() { return null; } @Override public Container getModelParent() { return null; } } public static class StructContainer extends Container { private final Container parent; public StructContainer(final int type, final Container parent, final WikitextAstNode astNode) { super(type, astNode); this.parent= parent; this.offset= astNode.getOffset(); this.length= astNode.getLength(); } @Override public ISourceUnit getSourceUnit() { return this.parent.getSourceUnit(); } @Override public boolean exists() { return this.parent.exists(); } @Override public boolean isReadOnly() { return this.parent.isReadOnly(); } @Override public ISourceStructElement getSourceParent() { return this.parent; } @Override public Container getModelParent() { return this.parent; } } public static class EmbeddedRef extends WikitextSourceElement implements IEmbeddedForeignElement { private final Container parent; private final String externType; private ISourceStructElement foreign; private final Embedded astNode; protected EmbeddedRef(final String externType, final Container parent, final Embedded astNode) { super(IModelElement.C1_EMBEDDED); this.externType= externType; this.parent= parent; this.astNode= 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(':'); sb.append(name); sb.append('#'); sb.append(this.occurrenceCount); return sb.toString(); } @Override public IElementName getElementName() { return (this.foreign != null) ? this.foreign.getElementName() : WikitextElementName.create(0, ""); } @Override public IRegion getNameSourceRange() { return (this.foreign != null) ? this.foreign.getNameSourceRange() : null; } @Override public ISourceUnit getSourceUnit() { return this.parent.getSourceUnit(); } @Override public boolean exists() { return this.parent.exists(); } @Override public boolean isReadOnly() { return this.parent.isReadOnly(); } @Override public IWikitextSourceElement getModelParent() { return this.parent; } @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 this.foreign; } @Override public ISourceStructElement getSourceParent() { return this.parent; } @Override public boolean hasSourceChildren(final Filter filter) { return (this.foreign != null && (filter == null || filter.include(this.foreign))); } @Override public List<? extends ISourceStructElement> getSourceChildren(final Filter filter) { return (this.foreign != null && (filter == null || filter.include(this.foreign))) ? ImCollections.<ISourceStructElement>newList(this.foreign) : NO_CHILDREN; } public void setForeign(final ISourceStructElement foreign) { this.foreign= foreign; } @Override public Object getAdapter(final Class required) { if (IAstNode.class.equals(required)) { return this.astNode; } { final Object adapter= super.getAdapter(required); if (adapter != null) { return adapter; } } return this.foreign.getAdapter(required); } @Override public int hashCode() { int h= (IModelElement.C1_EMBEDDED & MASK_C2) * this.externType.hashCode() + this.occurrenceCount; if (this.foreign != null) { h =+ this.foreign.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()) ) && ((this.foreign != null) ? this.foreign.equals(other.foreign) : null == other.foreign) ); } } private final int type; WikitextElementName name; int occurrenceCount; int offset; int length; protected WikitextSourceElement(final int type) { this.type= type; } @Override public final String getModelTypeId() { return WikitextModel.WIKIDOC_TYPE_ID; } @Override public final int getElementType() { return this.type; } @Override public IElementName getElementName() { return this.name; } @Override public String getId() { final String name= getElementName().getDisplayName(); final StringBuilder sb= new StringBuilder(name.length() + 16); sb.append(Integer.toHexString(getElementType() & MASK_C2)); sb.append(':'); sb.append(name); sb.append('#'); sb.append(this.occurrenceCount); return sb.toString(); } @Override public IRegion getSourceRange() { return this; } @Override public int getOffset() { return this.offset; } @Override public int getLength() { return this.length; } @Override public IRegion getDocumentationRange() { return null; } @Override public Object getAdapter(final Class adapter) { return null; } @Override public int hashCode() { return (this.type & MASK_C2) * getElementName().hashCode() + this.occurrenceCount; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!(obj instanceof WikitextSourceElement)) { return false; } final WikitextSourceElement other= (WikitextSourceElement) obj; return ( (this.type & MASK_C2) == (other.type & MASK_C2)) && (this.occurrenceCount == other.occurrenceCount) && ( ((this.type & MASK_C1) == C1_SOURCE) || (getSourceParent().equals(other.getSourceParent())) ) && (getElementName().equals(other.getElementName()) ); } }