gt: 			for (EAttribute attr: eAllAttributes) {
context: package org.nasdanika.doc.ecore;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  
  import org.apache.commons.lang3.StringEscapeUtils;
  import org.eclipse.emf.common.notify.Notifier;
  import org.eclipse.emf.common.util.TreeIterator;
  import org.eclipse.emf.ecore.EAnnotation;
  import org.eclipse.emf.ecore.EAttribute;
  import org.eclipse.emf.ecore.EClass;
  import org.eclipse.emf.ecore.EClassifier;
  import org.eclipse.emf.ecore.EOperation;
  import org.eclipse.emf.ecore.EPackage;
  import org.eclipse.emf.ecore.EParameter;
  import org.eclipse.emf.ecore.EReference;
  import org.eclipse.emf.ecore.resource.Resource;
  import org.eclipse.emf.ecore.resource.ResourceSet;
  import org.nasdanika.html.Accordion;
  import org.nasdanika.html.Bootstrap;
  import org.nasdanika.html.Bootstrap.Glyphicon;
  import org.nasdanika.html.Fragment;
  import org.nasdanika.html.HTMLFactory;
  import org.nasdanika.html.RowContainer.Row;
  import org.nasdanika.html.Table;
  import org.nasdanika.html.Tabs;
  import org.nasdanika.html.Tag;
  import org.nasdanika.html.TagName;
  
  import net.sourceforge.plantuml.FileFormat;
  import net.sourceforge.plantuml.FileFormatOption;
  import net.sourceforge.plantuml.SourceStringReader;
  
  public class EClassDocumentationGenerator extends EModelElementDocumentationGenerator<EClass> {
  	
  	public EClassDocumentationGenerator(EClass modelElement) {
  		super(modelElement);
  	}
  
  	/**
  	 * Generates PNG diagram.
  	 * @return
  	 * @throws IOException 
  	 */
  	public String generateDiagram(
  			boolean leftToRightDirection, 
  			String width, 
  			int depth, 
  			PlantUmlTextGenerator.RelationshipDirection relationshipDirection,
  			boolean appendAttributes,
  			boolean appendOperations,
  			OutputStream out) throws IOException {
  		
  		StringBuilder sb = new StringBuilder();
  		PlantUmlTextGenerator gen = new PlantUmlTextGenerator(sb, this::getEClassifierLocation, this::getFirstDocSentence) {
  			
  			@Override
  			protected Collection<EClass> getSubTypes(EClass eClass) {
  				return EClassDocumentationGenerator.this.getSubTypes(eClass);
  			}
  			
  			@Override
  			protected Collection<EClass> getReferrers(EClass eClass) {
  				return EClassDocumentationGenerator.this.getReferrers(eClass);
  			}
  			
  			@Override
  			protected boolean isAppendAttributes(EClass eClass) {
  				return appendAttributes;
  			}
  			
  			@Override
  			protected boolean isAppendOperations(EClass eClass) {
  				return appendOperations;
  			}
  			
  		};
  		gen.appendStartUml();
  		
  		if (leftToRightDirection) {
  			sb.append("left to right direction").append(System.lineSeparator());
  		}
  		
  		if (width != null) {
  			sb.append("scale ").append(width).append(" width").append(System.lineSeparator());
  		}
  						
  		gen.appendWithRelationships(Collections.singleton(getModelElement()), relationshipDirection, depth);
  		
  		gen.appendEndUml();
  		SourceStringReader reader = new SourceStringReader(sb.toString());
  		
  		FileFormatOption fileFormatOption = new FileFormatOption(FileFormat.PNG);
  		reader.outputImage(out, 0, fileFormatOption);
  		
  		return reader.getCMapData(0, fileFormatOption);
  	}
  		
  	@Override
  	public String generateDocumentation(String diagramCMap) {
  		HTMLFactory htmlFactory = getHtmlFactory();
  		Fragment ret = htmlFactory.fragment(htmlFactory.title("EClass "+getModelElement().getName()));
  		ret.content(htmlFactory.tag(TagName.h2, eClassifierIcon(getModelElement()), getModelElement().isAbstract() ? "<I>"+getModelElement().getName()+"</I>" : getModelElement().getName()));
  		StringBuilder modifiers = new StringBuilder();
  		if (getModelElement().isInterface()) {
  			modifiers.append("interface ");
  		} else {
  			if (getModelElement().isAbstract()) {
  				modifiers.append("abstract ");
  			}
  			modifiers.append("class ");
  		}
  		
  		ret.content(htmlFactory.div(javaDocLink(getModelElement().getInstanceClassName())).style("margin-bottom", "5px").style("font-family", "monospace"));
  	
  		Tabs tabs = htmlFactory.tabs();
  		ret.content(tabs);
  		tabs(tabs, diagramCMap);
  		return ret.toString();		
  	}
  	
  	protected int diagramTabDocLengthThreshold = 2500; 
  	
  	public void setDiagramTabDocLengthThreshold(int diagramTabDocLengthThreshold) {
  		this.diagramTabDocLengthThreshold = diagramTabDocLengthThreshold;
  	}
  	
  	public int getDiagramTabDocLengthThreshold() {
  		return diagramTabDocLengthThreshold;
  	}
  
  	/**
  	 * Returns true if documentation tab includes the diagram and there is no need in a diagram tab.
  	 * @param tabs
  	 * @return
  	 */
  	protected boolean documentationTab(Tabs tabs, String diagramCMap) {
  		HTMLFactory htmlFactory = getHtmlFactory();
  		Fragment ret = htmlFactory.fragment();
  		String doc = getModelDocumentation(getModelElement());
  		if (!isBlank(doc)) {
  			ret.content(htmlFactory.div(doc)
  					.addClass("markdown-body")
  					.style().margin().top("10px")
  					.style().margin().bottom("10px"));
  		}
  		
  		for (EAnnotation eAnnotation: getModelElement().getEAnnotations()) {
  			ret.content(documentAnnotation(eAnnotation));
  		}
  		
  		if (ret.isEmpty()) {
  			return false;
  		}
  		
  		tabs.item("Documentation", ret);
  		if (doc == null || doc.length() > getDiagramTabDocLengthThreshold()) {
  			return false;
  		}
  		
  		ret.content(htmlFactory.tag(TagName.img).attribute("src", getDiagramImageLocation()).attribute("usemap", "#plantuml_map"), diagramCMap);			
  		return true;
  	}
  	
  	/**
  	 * Override to return a list of sub-types of given EClass. 
  	 * This implementation returns all sub-types found in the EClass' current package. 
  	 * @param eClass
  	 * @return
  	 */
  	protected Collection<EClass> getSubTypes(EClass eClass) {
  		TreeIterator<?> acit;
  		Resource eResource = eClass.eResource();
  		if (eResource == null) {
  			EPackage ePackage = eClass.getEPackage();
  			if (ePackage == null) {
  				return Collections.emptySet();
  			}
  			acit = ePackage.eAllContents();
  		} else {
  			ResourceSet resourceSet = eResource.getResourceSet();
  			acit = resourceSet == null ? eResource.getAllContents() : eResource.getAllContents();
  		}
  		Set<EClass> ret = new HashSet<>();
  		acit.forEachRemaining(obj -> {
  			if (obj instanceof EClass && ((EClass) obj).getESuperTypes().contains(eClass)) {
  				ret.add((EClass) obj);
  			}
  		});
  		return ret;
  	}
  	
  	protected void subTypesTab(Tabs tabs) {
  		
  		Collection<EClass> subTypes = getSubTypes(getModelElement());
  		if (subTypes!=null && !subTypes.isEmpty()) {
  			Table stTable = getHtmlFactory().table().bordered();
  			Row hr = stTable.row().style(Bootstrap.Style.INFO);
  			hr.header("Name");
  			hr.header("Description");
  			for (EClass st: subTypes) {
  				Row stRow = stTable.row();
  				stRow.cell(eClassifierLink(st, false));
  				stRow.cell(getFirstDocSentence(st));
  			}
  			tabs.item("Subtypes", stTable);
  		}
  	}
  
  	protected void supertypesTab(Tabs tabs) {
  		if (!getModelElement().getESuperTypes().isEmpty()) {
  			Table stTable = getHtmlFactory().table().bordered();
  			Row hr = stTable.row().style(Bootstrap.Style.INFO);
  			hr.header("Name");
  			hr.header("Description");
  			for (EClass st: getModelElement().getESuperTypes()) {
  				Row stRow = stTable.row();
  				stRow.cell(eClassifierLink(st, false));
  				stRow.cell(getFirstDocSentence(st));
  			}
  			tabs.item("Supertypes", stTable);
  		}
  	}
  
  	protected void attributesTab(Tabs tabs) {
  		
  		HTMLFactory htmlFactory = getHtmlFactory();
  		
  		Tag attributeIcon = htmlFactory.tag(TagName.img)
  				.attribute("src", getIconsBaseLocation()+"EAttribute.gif")
  				.style("margin-right", "5px");
  		
  		List<EAttribute> eAllAttributes = new ArrayList<>(getModelElement().getEAllAttributes());
  		if (!eAllAttributes.isEmpty()) {
  			Collections.sort(eAllAttributes, NAMED_ELEMENT_COMPARATOR);
  			Accordion attributesAccordion = htmlFactory.accordion();
  