package com.github.setial.intellijjavadocs.utils; import com.github.setial.intellijjavadocs.model.JavaDoc; import com.github.setial.intellijjavadocs.model.JavaDocTag; import com.github.setial.intellijjavadocs.transformation.JavaDocBuilder; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementFactory; import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef; import com.intellij.psi.impl.source.javadoc.PsiDocParamRef; import com.intellij.psi.impl.source.javadoc.PsiDocTagValueImpl; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTag; import com.intellij.psi.javadoc.PsiDocTagValue; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * The type Java doc utils. * * @author Sergey Timofiychuk */ public class JavaDocUtils { private static final List<String> MERGE_TAG_NAMES = Arrays.asList("param", "throws"); /** * Convert java doc. * * @param javadoc the Javadoc * @return the String */ @NotNull public static String convertJavaDoc(@NotNull JavaDoc javadoc) { JavaDocBuilder builder = new JavaDocBuilder(); builder.createDefaultJavaDoc(javadoc); return builder.build(); } /** * Merge java docs. * * @param oldJavaDoc the Old java doc * @param newJavaDoc the New java doc * @return the Java doc */ @NotNull public static JavaDoc mergeJavaDocs(@NotNull JavaDoc oldJavaDoc, @NotNull JavaDoc newJavaDoc) { List<String> description = oldJavaDoc.getDescription(); if (descriptionIsEmpty(description)) { description = newJavaDoc.getDescription(); } Map<String, List<JavaDocTag>> oldTags = oldJavaDoc.getTags(); Map<String, List<JavaDocTag>> newTags = newJavaDoc.getTags(); Map<String, List<JavaDocTag>> tags = new LinkedHashMap<String, List<JavaDocTag>>(); List<String> processedTagNames = new LinkedList<String>(); for (Entry<String, List<JavaDocTag>> newTagsEntry : newTags.entrySet()) { String name = newTagsEntry.getKey(); if (!tags.containsKey(name)) { tags.put(name, new LinkedList<JavaDocTag>()); } List<JavaDocTag> tagsEntry = newTagsEntry.getValue(); for (JavaDocTag tag : tagsEntry) { if (oldTags.containsKey(name)) { // the case when old tag exists List<JavaDocTag> oldTagsEntry = oldTags.get(name); JavaDocTag oldTag; if (!MERGE_TAG_NAMES.contains(name)) { oldTag = oldTagsEntry.get(0); } else { oldTag = findOldTag(oldTagsEntry, tag.getValue(), tag.getRefParam()); } if (oldTag != null) { // the case when old tag is ok tags.get(name).add(mergeJavaDocTag(oldTag, tag)); } else { tags.get(name).add(tag); } } else { // the case when old tag has been removed tags.get(name).add(tag); } } processedTagNames.add(name); } // add old tags that were not processed for (Entry<String, List<JavaDocTag>> entry : oldTags.entrySet()) { String name = entry.getKey(); if (!processedTagNames.contains(name)) { tags.put(name, entry.getValue()); } } return new JavaDoc(description, tags); } /** * Merge java doc tag. * * @param oldJavaDocTag the Old java doc tag * @param newJavaDocTag the New java doc tag * @return the Java doc tag */ @NotNull public static JavaDocTag mergeJavaDocTag(@NotNull JavaDocTag oldJavaDocTag, @NotNull JavaDocTag newJavaDocTag) { List<String> description = oldJavaDocTag.getDescription(); if (descriptionIsEmpty(description)) { description = newJavaDocTag.getDescription(); } return new JavaDocTag(oldJavaDocTag.getRefParam(), oldJavaDocTag.getValue(), description); } /** * Creates the java doc tag. * * @param docTag the Doc tag * @return the Java doc tag */ @NotNull public static JavaDocTag createJavaDocTag(@NotNull PsiDocTag docTag) { String docTagRefParam = findDocTagRefParam(docTag); String docTagValue = findDocTagValue(docTag); List<String> docTagDescription = findDocTagDescription(docTag, docTagRefParam, docTagValue); return new JavaDocTag( docTagRefParam, docTagValue, docTagDescription); } /** * Creates the java doc. * * @param docComment the Doc comment * @return the Java doc */ @NotNull public static JavaDoc createJavaDoc(@NotNull PsiDocComment docComment) { return new JavaDoc( findDocDescription(docComment), findDocTags(docComment)); } /** * Find java doc description. * * @param docComment the Doc comment * @return the description */ @NotNull public static List<String> findDocDescription(@NotNull PsiDocComment docComment) { List<String> descriptions = new LinkedList<String>(); PsiElement[] descriptionElements = docComment.getDescriptionElements(); for (PsiElement descriptionElement : descriptionElements) { descriptions.add(descriptionElement.getText()); } return descriptions; } /** * Find doc tags. * * @param docComment the Doc comment * @return the javadoc tags */ @NotNull public static Map<String, List<JavaDocTag>> findDocTags(@NotNull PsiDocComment docComment) { Map<String, List<JavaDocTag>> tags = new LinkedHashMap<String, List<JavaDocTag>>(); PsiDocTag[] docTags = docComment.getTags(); for (PsiDocTag docTag : docTags) { String name = docTag.getName(); if (!tags.containsKey(name)) { tags.put(name, new LinkedList<JavaDocTag>()); } tags.get(name).add(createJavaDocTag(docTag)); } return tags; } /** * Find doc tag ref param. * * @param docTag the Doc tag * @return the javadoc's tag ref parameter */ @Nullable public static String findDocTagRefParam(@NotNull PsiDocTag docTag) { String refParam = null; for (PsiElement element : docTag.getDataElements()) { if (element instanceof PsiDocParamRef || element instanceof PsiDocMethodOrFieldRef) { refParam = element.getText(); break; } } return refParam; } /** * Find doc tag value. * * @param docTag the Doc tag * @return the javadoc's tag value */ @Nullable public static String findDocTagValue(@NotNull PsiDocTag docTag) { String value = null; for (PsiElement element : docTag.getDataElements()) { if (element instanceof PsiDocTagValue) { value = element.getText(); break; } } return value; } /** * Find doc tag description. * * @param docTag the Doc tag * @param docTagRefParam the doc tag ref param * @param docTagValue the doc tag value * @return the javadoc's tag descriptions */ @NotNull public static List<String> findDocTagDescription(@NotNull PsiDocTag docTag, String docTagRefParam, String docTagValue) { List<String> descriptions = new LinkedList<String>(); List<PsiElement> elements = new LinkedList<PsiElement>(Arrays.asList(docTag.getDataElements())); for (Iterator<PsiElement> iterator = elements.iterator(); iterator.hasNext(); ) { PsiElement element = iterator.next(); removeValueIfAssignableType(docTagRefParam, PsiDocParamRef.class, iterator, element); removeValueIfAssignableType(docTagValue, PsiDocTagValueImpl.class, iterator, element); } StringBuilder descriptionBuilder = new StringBuilder(); for (PsiElement element : elements) { descriptionBuilder.append(element.getText()); } descriptions.add(descriptionBuilder.toString()); return descriptions; } private static void removeValueIfAssignableType(String value, Class<? extends PsiElement> valueType, Iterator<PsiElement> iterator, PsiElement element) { if (value != null && element.getClass().isAssignableFrom(valueType) && element.getText().equals(value)) { iterator.remove(); } } /** * Converts string to java doc. * * @param javaDocText the Java doc text * @param psiElementFactory the Psi element factory * @return the Java doc */ @Nullable public static JavaDoc toJavaDoc(@Nullable String javaDocText, @NotNull PsiElementFactory psiElementFactory) { JavaDoc result = null; if (StringUtils.isNotBlank(javaDocText)) { PsiDocComment javaDocComment = psiElementFactory.createDocCommentFromText(javaDocText); result = createJavaDoc(javaDocComment); } return result; } @Nullable private static JavaDocTag findOldTag(List<JavaDocTag> oldTagsEntry, String value, String refParam) { JavaDocTag result = null; for (JavaDocTag oldTag : oldTagsEntry) { if (StringUtils.equals(oldTag.getValue(), value) && StringUtils.equals(oldTag.getRefParam(), refParam)) { result = oldTag; break; } } return result; } private static boolean descriptionIsEmpty(List<String> description) { boolean result = true; if (!CollectionUtils.isEmpty(description)) { for (String item : description) { result = result && StringUtils.isBlank(item); } } return result; } private JavaDocUtils() { } }