package hudson.plugins.analysis.views; import javax.annotation.Nonnull; import java.util.Collection; import java.util.Map; import org.apache.commons.lang.StringUtils; import com.google.common.collect.Maps; import hudson.model.AbstractBuild; import hudson.model.Item; import hudson.model.Run; import hudson.plugins.analysis.Messages; import hudson.plugins.analysis.core.BuildResult; import hudson.plugins.analysis.core.ResultAction; import hudson.plugins.analysis.util.Compatibility; import hudson.plugins.analysis.util.model.AnnotationContainer; import hudson.plugins.analysis.util.model.AnnotationsLabelProvider; import hudson.plugins.analysis.util.model.DefaultAnnotationContainer; import hudson.plugins.analysis.util.model.FileAnnotation; import hudson.plugins.analysis.util.model.LineRange; /** * Creates detail objects for the selected element of a annotation container. * * @author Ulli Hafner */ @SuppressWarnings("deprecation") public class DetailFactory { /** Default detail builder class. */ private static final DetailFactory DEFAULT_DETAIL_BUILDER = new DetailFactory(); /** Maps plug-ins to detail builders. */ private static Map<Class<? extends ResultAction<? extends BuildResult>>, DetailFactory> factories = Maps.newHashMap(); /** * Creates a new detail builder. * * @param actionType * the type of the action (i.e., the plug-in) to get the detail * builder for * @return the detail builder */ public static DetailFactory create(final Class<? extends ResultAction<? extends BuildResult>> actionType) { if (factories.containsKey(actionType)) { return factories.get(actionType); } return DEFAULT_DETAIL_BUILDER; } /** * Sets the detail builder class to the specified value. * * @param actionType * the type of the action (i.e., the plug-in) to set the detail * builder for * @param detailBuilder * the value to set */ public static void addDetailBuilder(final Class<? extends ResultAction<? extends BuildResult>> actionType, final DetailFactory detailBuilder) { synchronized (factories) { factories.put(actionType, detailBuilder); } } /** * Returns a detail object for the selected element of the specified * annotation container. The details will include the new and fixed warnings * trends as well as the errors report. * * @param link * the link to identify the sub page to show * @param owner * the build as owner of the detail page * @param container * the annotation container to get the details for * @param fixedAnnotations * the annotations fixed in this build * @param newAnnotations * the annotations new in this build * @param errors * the errors in this build * @param defaultEncoding * the default encoding to be used when reading and parsing files * @param displayName * the name of the selected object * @return the dynamic result of this module detail view */ // CHECKSTYLE:OFF @SuppressWarnings("deprecation") public Object createTrendDetails(final String link, @Nonnull final Run<?, ?> owner, final AnnotationContainer container, final Collection<FileAnnotation> fixedAnnotations, final Collection<FileAnnotation> newAnnotations, final Collection<String> errors, final String defaultEncoding, final String displayName) { // CHECKSTYLE:ON if (owner instanceof AbstractBuild && Compatibility.isOverridden(DetailFactory.class, getClass(), "createTrendDetails", String.class, AbstractBuild.class, AnnotationContainer.class, Collection.class, Collection.class, Collection.class, String.class, String.class)) { return createTrendDetails(link, (AbstractBuild<?, ?>) owner, container, fixedAnnotations, newAnnotations, errors, defaultEncoding, displayName); } else { AnnotationContainer detail; if ("fixed".equals(link)) { detail = createFixedWarningsDetail(owner, fixedAnnotations, defaultEncoding, displayName); } else if ("new".equals(link)) { detail = new NewWarningsDetail(owner, this, newAnnotations, defaultEncoding, displayName); } else if ("error".equals(link)) { return new ErrorDetail(owner, errors); } else if (link.startsWith("tab.new")) { detail = createTabDetail(owner, newAnnotations, createGenericTabUrl(link), defaultEncoding); } else if (link.startsWith("tab.fixed")) { detail = createTabDetail(owner, fixedAnnotations, createGenericTabUrl(link), defaultEncoding); } else { return createDetails(link, owner, container, defaultEncoding, displayName); } attachLabelProvider(detail); return detail; } } /** * Returns the default label provider that is used to visualize the build result (i.e., the tab labels). * * @return the default label probider * @since 1.69 */ protected void attachLabelProvider(final AnnotationContainer container) { container.setLabelProvider(new AnnotationsLabelProvider(container.getPackageCategoryTitle())); } /** * Returns a detail object for the selected element of the specified * annotation container. * * @param link * the link to identify the sub page to show * @param owner * the build as owner of the detail page * @param container * the annotation container to get the details for * @param defaultEncoding * the default encoding to be used when reading and parsing files * @param displayName * the name of the selected object * @return the dynamic result of this module detail view */ public Object createDetails(final String link, @Nonnull final Run<?, ?> owner, final AnnotationContainer container, final String defaultEncoding, final String displayName) { if (owner instanceof AbstractBuild && Compatibility.isOverridden(DetailFactory.class, getClass(), "createDetails", String.class, AbstractBuild.class, AnnotationContainer.class, String.class, String.class)) { return createDetails(link, (AbstractBuild<?, ?>) owner, container, defaultEncoding, displayName); } else { PriorityDetailFactory factory = new PriorityDetailFactory(this); AnnotationContainer detail = null; if (factory.isPriority(link)) { detail = factory.create(link, owner, container, defaultEncoding, displayName); } else if (link.startsWith("module.")) { detail = new ModuleDetail(owner, this, container.getModule(createHashCode(link, "module.")), defaultEncoding, displayName); } else if (link.startsWith("package.")) { detail = new PackageDetail(owner, this, container.getPackage(createHashCode(link, "package.")), defaultEncoding, displayName); } else if (link.startsWith("file.")) { detail = new FileDetail(owner, this, container.getFile(createHashCode(link, "file.")), defaultEncoding, displayName); } else if (link.startsWith("tab.")) { detail = createTabDetail(owner, container.getAnnotations(), createGenericTabUrl(link), defaultEncoding); } else if (link.startsWith("source.")) { owner.checkPermission(Item.WORKSPACE); FileAnnotation annotation = container.getAnnotation(StringUtils.substringAfter(link, "source.")); if (annotation.isInConsoleLog()) { LineRange lines = annotation.getLineRanges().iterator().next(); return new ConsoleDetail(owner, lines.getStart(), lines.getEnd()); } else { return new SourceDetail(owner, annotation, defaultEncoding); } } else if (link.startsWith("category.")) { DefaultAnnotationContainer category = container.getCategory(createHashCode(link, "category.")); detail = createAttributeDetail(owner, category, displayName, Messages.CategoryDetail_header(), defaultEncoding); } else if (link.startsWith("type.")) { DefaultAnnotationContainer type = container.getType(createHashCode(link, "type.")); detail = createAttributeDetail(owner, type, displayName, Messages.TypeDetail_header(), defaultEncoding); } if (detail != null) { attachLabelProvider(detail); } return detail; } } /** * Creates a generic detail tab with the specified link. * * @param owner * the build as owner of the detail page * @param annotations * the annotations to display * @param displayName * the name of the view * @param header * the bread crumb name * @param defaultEncoding * the default encoding to be used when reading and parsing files * @return the detail view */ protected AttributeDetail createAttributeDetail(@Nonnull final Run<?, ?> owner, final DefaultAnnotationContainer annotations, final String displayName, final String header, final String defaultEncoding) { if (owner instanceof AbstractBuild && Compatibility.isOverridden(DetailFactory.class, getClass(), "createAttributeDetail", AbstractBuild.class, DefaultAnnotationContainer.class, String.class, String.class, String.class)) { return createAttributeDetail((AbstractBuild<?, ?>) owner, annotations, displayName, header, defaultEncoding); } else { return new AttributeDetail(owner, this, annotations.getAnnotations(), defaultEncoding, displayName, header + " " + annotations.getName()); } } /** * Creates a generic detail tab with the specified link. * * @param owner * the build as owner of the detail page * @param annotations * the annotations to display * @param url * the URL for the details view * @param defaultEncoding * the default encoding to be used when reading and parsing files * @return the detail view */ protected TabDetail createTabDetail(@Nonnull final Run<?, ?> owner, final Collection<FileAnnotation> annotations, final String url, final String defaultEncoding) { if (owner instanceof AbstractBuild && Compatibility.isOverridden(DetailFactory.class, getClass(), "createTabDetail", AbstractBuild.class, Collection.class, String.class, String.class)) { return createTabDetail((AbstractBuild<?, ?>) owner, annotations, url, defaultEncoding); } else { return new TabDetail(owner, this, annotations, url, defaultEncoding); } } /** * Creates a generic fixed warnings detail tab with the specified link. * * @param owner * the build as owner of the detail page * @param fixedAnnotations * the annotations to display * @param defaultEncoding * the default encoding to be used when reading and parsing files * @param displayName * the name of the view * @return the detail view */ protected FixedWarningsDetail createFixedWarningsDetail(@Nonnull final Run<?, ?> owner, final Collection<FileAnnotation> fixedAnnotations, final String defaultEncoding, final String displayName) { if (owner instanceof AbstractBuild && Compatibility.isOverridden(DetailFactory.class, getClass(), "createFixedWarningsDetail", AbstractBuild.class, Collection.class, String.class, String.class)) { return createFixedWarningsDetail((AbstractBuild<?, ?>) owner, fixedAnnotations, defaultEncoding, displayName); } else { return new FixedWarningsDetail(owner, this, fixedAnnotations, defaultEncoding, displayName); } } /** * Creates the actual URL from the synthetic link. * * @param link * the link * @return the actual URL */ private String createGenericTabUrl(final String link) { return StringUtils.substringAfter(link, "tab.") + ".jelly"; } /** * Extracts the hash code from the given link stripping of the given prefix. * * @param link the whole link * @param prefix the prefix to remove * * @return the hash code */ private int createHashCode(final String link, final String prefix) { try { return Integer.parseInt(StringUtils.substringAfter(link, prefix)); } catch (NumberFormatException e) { return -1; // non-existent ID } } /** * Creates a generic detail tab with the specified link. * * @param owner * the build as owner of the detail page * @param annotations * the annotations to display * @param url * the URL for the details view * @param defaultEncoding * the default encoding to be used when reading and parsing files * @return the detail view * @deprecated use {@link #createTabDetail(Run, Collection, String, String)} instead */ @Deprecated protected TabDetail createTabDetail(final AbstractBuild<?, ?> owner, final Collection<FileAnnotation> annotations, final String url, final String defaultEncoding) { return new TabDetail(owner, this, annotations, url, defaultEncoding); } /** * Creates a generic fixed warnings detail tab with the specified link. * * @param owner * the build as owner of the detail page * @param fixedAnnotations * the annotations to display * @param defaultEncoding * the default encoding to be used when reading and parsing files * @param displayName * the name of the view * @return the detail view * @deprecated use {@link #createFixedWarningsDetail(Run, Collection, String, String)} instead */ @Deprecated protected FixedWarningsDetail createFixedWarningsDetail(final AbstractBuild<?, ?> owner, final Collection<FileAnnotation> fixedAnnotations, final String defaultEncoding, final String displayName) { return new FixedWarningsDetail(owner, this, fixedAnnotations, defaultEncoding, displayName); } /** * @deprecated use {@link #createAttributeDetail(Run, DefaultAnnotationContainer, String, String, String)} instead */ @Deprecated protected AttributeDetail createAttributeDetail(final AbstractBuild<?, ?> owner, final DefaultAnnotationContainer annotations, final String displayName, final String header, final String defaultEncoding) { return new AttributeDetail(owner, this, annotations.getAnnotations(), defaultEncoding, displayName, header + " " + annotations.getName()); } /** * @deprecated use {@link #createTrendDetails(String, Run, AnnotationContainer, Collection, Collection, Collection, String, String)} instead */ @Deprecated public Object createTrendDetails(final String link, final AbstractBuild<?, ?> owner, final AnnotationContainer container, final Collection<FileAnnotation> fixedAnnotations, final Collection<FileAnnotation> newAnnotations, final Collection<String> errors, final String defaultEncoding, final String displayName) { AnnotationContainer detail; if ("fixed".equals(link)) { detail = createFixedWarningsDetail(owner, fixedAnnotations, defaultEncoding, displayName); } else if ("new".equals(link)) { detail = new NewWarningsDetail(owner, this, newAnnotations, defaultEncoding, displayName); } else if ("error".equals(link)) { return new ErrorDetail(owner, errors); } else if (link.startsWith("tab.new")) { detail = createTabDetail(owner, newAnnotations, createGenericTabUrl(link), defaultEncoding); } else if (link.startsWith("tab.fixed")) { detail = createTabDetail(owner, fixedAnnotations, createGenericTabUrl(link), defaultEncoding); } else { return createDetails(link, owner, container, defaultEncoding, displayName); } attachLabelProvider(detail); return detail; } /** * @deprecated use {@link #createDetails(String, Run, AnnotationContainer, String, String)} instead */ @Deprecated public Object createDetails(final String link, final AbstractBuild<?, ?> owner, final AnnotationContainer container, final String defaultEncoding, final String displayName) { PriorityDetailFactory factory = new PriorityDetailFactory(this); AnnotationContainer detail = null; if (factory.isPriority(link)) { detail = factory.create(link, owner, container, defaultEncoding, displayName); } else if (link.startsWith("module.")) { detail = new ModuleDetail(owner, this, container.getModule(createHashCode(link, "module.")), defaultEncoding, displayName); } else if (link.startsWith("package.")) { detail = new PackageDetail(owner, this, container.getPackage(createHashCode(link, "package.")), defaultEncoding, displayName); } else if (link.startsWith("file.")) { detail = new FileDetail(owner, this, container.getFile(createHashCode(link, "file.")), defaultEncoding, displayName); } else if (link.startsWith("tab.")) { detail = createTabDetail(owner, container.getAnnotations(), createGenericTabUrl(link), defaultEncoding); } else if (link.startsWith("source.")) { owner.checkPermission(Item.WORKSPACE); FileAnnotation annotation = container.getAnnotation(StringUtils.substringAfter(link, "source.")); if (annotation.isInConsoleLog()) { LineRange lines = annotation.getLineRanges().iterator().next(); return new ConsoleDetail(owner, lines.getStart(), lines.getEnd()); } else { return new SourceDetail(owner, annotation, defaultEncoding); } } else if (link.startsWith("category.")) { DefaultAnnotationContainer category = container.getCategory(createHashCode(link, "category.")); detail = createAttributeDetail(owner, category, displayName, Messages.CategoryDetail_header(), defaultEncoding); } else if (link.startsWith("type.")) { DefaultAnnotationContainer type = container.getType(createHashCode(link, "type.")); detail = createAttributeDetail(owner, type, displayName, Messages.TypeDetail_header(), defaultEncoding); } if (detail != null) { attachLabelProvider(detail); } return detail; } }