/******************************************************************************* * Copyright (c) 2013 Bruno Medeiros and other Contributors. * 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: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package dtool.ast.declarations; import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; import melnorme.lang.tooling.EProtection; import melnorme.lang.tooling.ast.CommonASTNode; import melnorme.lang.tooling.ast.IASTNode; import melnorme.lang.tooling.ast.IASTVisitor; import melnorme.lang.tooling.ast.util.ASTCodePrinter; import melnorme.lang.tooling.ast.util.NodeList; import melnorme.lang.tooling.ast.util.NodeVector; import melnorme.lang.tooling.ast_actual.ASTNode; import melnorme.lang.tooling.ast_actual.ASTNodeTypes; import melnorme.lang.tooling.engine.scoping.INonScopedContainer; import melnorme.utilbox.collections.ArrayList2; import melnorme.utilbox.misc.IteratorUtil; import dtool.ast.definitions.CommonDefinition; import dtool.ast.statements.IStatement; /** * Attribute declarations * * Technicaly DMD doesn't accept certain attributes as statements (such as protection, align), * but structurally we allow it, even though a syntax or semantic error may still be issued. */ public class DeclarationAttrib extends ASTNode implements INonScopedContainer, IDeclaration, IStatement { public static enum AttribBodySyntax { SINGLE_DECL, BRACE_BLOCK, COLON } public final NodeVector<Attribute> attributes; public final AttribBodySyntax bodySyntax; public final ASTNode body; // Note: can be DeclList public DeclarationAttrib(NodeVector<Attribute> attributes, AttribBodySyntax bodySyntax, ASTNode bodyDecls) { this.attributes = parentize(assertNotNull(attributes)); this.bodySyntax = assertNotNull(bodySyntax); this.body = parentize(bodyDecls); localAnalysis(); } @Override public ASTNodeTypes getNodeType() { return ASTNodeTypes.DECLARATION_ATTRIB; } @Override public void visitChildren(IASTVisitor visitor) { acceptVisitor(visitor, attributes); acceptVisitor(visitor, body); } @Override protected CommonASTNode doCloneTree() { return new DeclarationAttrib(clone(attributes), bodySyntax, clone(body)); } @Override public void toStringAsCode(ASTCodePrinter cp) { cp.appendList(attributes, " ", true); cp.append(bodySyntax == AttribBodySyntax.COLON, " :\n"); cp.append(body); } @Override public Iterable<? extends IASTNode> getMembersIterable() { return getBodyIterable(body); } public static Iterable<ASTNode> getBodyIterable(ASTNode body) { if(body == null) { return IteratorUtil.<ASTNode>emptyIterable(); } if(body instanceof NodeList<?>) { return ((NodeList<?>) body).nodes.upcastTypeParameter(); } // TODO save body node collection return ArrayList2.create(body); } /** * If this declaration attrib contains only a single declaration, return it, otherwise return null */ public IDeclaration getSingleDeclaration() { if(bodySyntax != AttribBodySyntax.SINGLE_DECL) { return null; } if(body instanceof IDeclaration) { return (IDeclaration) body; } return null; } protected void localAnalysis() { for (Attribute attribute : attributes) { if(attribute instanceof AttribBasic) { AttribBasic attribBasic = (AttribBasic) attribute; applyBasicAttributes(attribBasic, this); } } for (int ix = attributes.size() - 1; ix >= 0; ix--) { Attribute attribute = attributes.get(ix); if(attribute instanceof AttribProtection) { AttribProtection attribProtection = (AttribProtection) attribute; applyProtectionAttributes(attribProtection.protection, this); break; // last atribute takes precedence } } } // TODO have CommonDefinition fetch attributes upwards, // instead of the other way around protected void applyBasicAttributes(AttribBasic attribute, INonScopedContainer block) { for (IASTNode node : block.getMembersIterable()) { if(node instanceof CommonDefinition) { CommonDefinition def = (CommonDefinition) node; def.setAttribute(attribute); } else if(node instanceof INonScopedContainer) { applyBasicAttributes(attribute, (INonScopedContainer) node); } } } protected void applyProtectionAttributes(EProtection protection, INonScopedContainer block) { for (IASTNode descendantNode : block.getMembersIterable()) { if(anotherProtectionAttribPresent(descendantNode)) { continue; // Do not descend, other attrib takes precedence } if(descendantNode instanceof CommonDefinition) { CommonDefinition def = (CommonDefinition) descendantNode; def.setProtection(protection); } else if(descendantNode instanceof DeclarationImport && protection == EProtection.PUBLIC) { DeclarationImport declImport = (DeclarationImport) descendantNode; declImport.isPublicImport = true; } else if(descendantNode instanceof INonScopedContainer) { applyProtectionAttributes(protection, (INonScopedContainer) descendantNode); } } } public boolean anotherProtectionAttribPresent(IASTNode node) { if(node instanceof DeclarationAttrib) { DeclarationAttrib declAttrib = (DeclarationAttrib) node; for (Attribute attrib : declAttrib.attributes) { if(attrib instanceof AttribProtection) return true; } } return false; } }