content: /* 
   * Kodkod -- Copyright (c) 2005-present, Emina Torlak
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  package kodkod.doc;
  
  
  import java.util.Map;
  
  import com.sun.javadoc.Doc;
  import com.sun.javadoc.MethodDoc;
  import com.sun.javadoc.Tag;
  import com.sun.tools.doclets.formats.html.markup.RawHtml;
  import com.sun.tools.doclets.internal.toolkit.Content;
  import com.sun.tools.doclets.internal.toolkit.taglets.InheritableTaglet;
  import com.sun.tools.doclets.internal.toolkit.taglets.Taglet;
  import com.sun.tools.doclets.internal.toolkit.taglets.TagletWriter;
  import com.sun.tools.doclets.internal.toolkit.util.DocFinder;
  import com.sun.tools.doclets.internal.toolkit.util.DocletConstants;
  
  /**
   * Represents a taglet for specification tags:  
   * <code>@specfield</code>, <code>@invariant</code>,
   * <code>@ensures</code>, and <code>@requires</code>.
   * 
   * @specfield name: String
   * 
   * @author Emina Torlak
   */
  public final class SpecificationTaglet implements InheritableTaglet {
  	private static final int TYPE = 1, FIELD = 2, METHOD = 4, CONSTRUCTOR = 8, PACKAGE = 16, OVERVIEW = 32;
  	private final String tagName, tagHeader;
  	private int tagLocations;
  
  	/**
  	 * Constructs a specification taglet for the given tag name, using the
  	 * given header and location specifier.
  	 */
  	private SpecificationTaglet(String tagName, String tagHeader, int tagLocations) {
  		this.tagName = tagName;
  		this.tagHeader = tagHeader;
  		this.tagLocations = tagLocations;
  	}
  
  	/**
  	 * Register this Taglet.
  	 * @param tagletMap  the map to register this tag to.
  	 */
  	public static void register(Map<String, Taglet> tagletMap) {
  		register(tagletMap, new SpecificationTaglet("specfield", "Specfields:", TYPE));
  		register(tagletMap, new SpecificationTaglet("requires", "Requires:", METHOD+CONSTRUCTOR));
  		register(tagletMap, new SpecificationTaglet("ensures", "Ensures:", METHOD+CONSTRUCTOR));
  		register(tagletMap, new SpecificationTaglet("invariant", "Invariants:", TYPE+METHOD+CONSTRUCTOR+FIELD));
  		}
  
  	/**
  	 * Adds the given taglet to the map.
  	 */
  	private static void register(Map<String, Taglet> tagletMap, Taglet taglet) {
  		final Taglet current = tagletMap.get(taglet.getName());
  		if (current != null)
  			tagletMap.remove(taglet.getName());
  		tagletMap.put(taglet.getName(), taglet);
  
  	}
  
  	/**
  	 * {@inheritDoc}
  	 * @see com.sun.tools.doclets.internal.toolkit.taglets.Taglet#getName()
  	 */
  	@Override
  	public String getName() { return tagName; }
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public void inherit(DocFinder.Input input, DocFinder.Output output) {
  		if (input.element != null && input.element.isMethod()) {
  			final Tag[] tags = input.element.tags(tagName);
  			if (tags.length > 0) {
  				output.holder = input.element;
  				output.holderTag = tags[0];
  				output.inlineTags = input.isFirstSentence ?
  						tags[0].firstSentenceTags() : tags[0].inlineTags();
  			} 
  		} 
  	}
  
  	/**
  	 * Appends the formatted definition header to the given buffer.
  	 * @return out
  	 */
  	private StringBuilder writeHeader(StringBuilder out) {
  		return out.append(DocletConstants.NL)
  				.append("<DT><B>").append(tagHeader).append("</B></DT>");
  	}
  	
  	/**
  	 * Appends the formatted definition tag to the given buffer.
  	 * @return out
  	 */
  	private StringBuilder writeTag(StringBuilder out, Tag tag, TagletWriter writer) {
  		return out.append(DocletConstants.NL)
  				.append("<DD><CODE>")
  				.append(writer.commentTagsToOutput(tag, null, tag.inlineTags(), false))
  				.append("</CODE></DD>");
  	}
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public Content getTagletOutput(Tag tag, TagletWriter writer) throws IllegalArgumentException {
  		final StringBuilder out = writeTag(writeHeader(new StringBuilder()), tag, writer);
  		return new RawHtml(out.toString());
  	}
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public Content getTagletOutput(Doc doc, TagletWriter writer) throws IllegalArgumentException {
  		Tag[] tags = doc.tags(getName());
  		if (tags.length==0 && doc instanceof MethodDoc) { // inherit if necessary and possible
  			final DocFinder.Output inheritedDoc = DocFinder.search(new DocFinder.Input((MethodDoc) doc, this));
  			tags = inheritedDoc.holderTag == null ? tags : new Tag[] {inheritedDoc.holderTag};
  		}
  		if (tags.length==0)
  			return null;
  		final StringBuilder out = writeHeader(new StringBuilder());
  		for(Tag tag : tags) { 
  			writeTag(out, tag, writer);
  		}
  		return new RawHtml(out.toString());
  	}
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public boolean inConstructor() { return (tagLocations & CONSTRUCTOR) != 0; }
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public boolean inField() { return (tagLocations & FIELD) != 0; }
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public boolean inMethod() { return (tagLocations & METHOD) != 0; } 
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public boolean inOverview() { return (tagLocations & OVERVIEW) != 0; }
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public boolean inPackage() { return (tagLocations & PACKAGE) != 0; }
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public boolean inType() { return (tagLocations & TYPE) != 0; }
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public boolean isInlineTag() { return false; }
  
  
  }
  