private void notify(
     @NotNull String text, @NotNull Key outputType, @NotNull StringBuilder lastLine) {
   // text is not more than one line - either one line or part of the line
   if (StringUtil.endsWith(text, "\n")) {
     // we have full line - notify listeners
     super.notifyTextAvailable(text, resolveOutputType(text, outputType));
   } else {
     // save line part to lastLine
     lastLine.append(text);
   }
 }
 private static void processDots(StringBuilder result, int dots, int start) {
   if (dots == 2) {
     int pos = -1;
     if (!StringUtil.endsWith(result, "/../") && !StringUtil.equals(result, "../")) {
       pos = StringUtil.lastIndexOf(result, '/', start, result.length() - 1);
       if (pos >= 0) {
         ++pos; // separator found, trim to next char
       } else if (start > 0) {
         pos = start; // path is absolute, trim to root ('/..' -> '/')
       } else if (result.length() > 0) {
         pos = 0; // path is relative, trim to default ('a/..' -> '')
       }
     }
     if (pos >= 0) {
       result.delete(pos, result.length());
     } else {
       result.append("../"); // impossible to traverse, keep as-is
     }
   } else if (dots != 1) {
     StringUtil.repeatSymbol(result, '.', dots);
     result.append('/');
   }
 }
 private void calculateMinEditSequence(
     String hostText, String newText, String[] result, int i, int j) {
   synchronized (myLock) {
     String rangeText1 = getRangeText(hostText, i);
     if (i == j) {
       result[i] = rangeText1.equals(newText) ? null : newText;
       return;
     }
     if (StringUtil.startsWith(newText, rangeText1)) {
       result[i] = null; // no change
       calculateMinEditSequence(
           hostText, newText.substring(rangeText1.length()), result, i + 1, j);
       return;
     }
     String rangeText2 = getRangeText(hostText, j);
     if (StringUtil.endsWith(newText, rangeText2)) {
       result[j] = null; // no change
       calculateMinEditSequence(
           hostText,
           newText.substring(0, newText.length() - rangeText2.length()),
           result,
           i,
           j - 1);
       return;
     }
     if (i + 1 == j) {
       String suffix = myShreds.get(i).getSuffix();
       String prefix = myShreds.get(j).getPrefix();
       String separator = suffix + prefix;
       if (!separator.isEmpty()) {
         int sep = newText.indexOf(separator);
         assert sep != -1;
         result[i] = newText.substring(0, sep + suffix.length());
         result[j] = newText.substring(sep + suffix.length() + prefix.length(), newText.length());
         return;
       }
       String commonPrefix = StringUtil.commonPrefix(rangeText1, newText);
       result[i] = commonPrefix;
       result[j] = newText.substring(commonPrefix.length());
       return;
     }
     String middleText = getRangeText(hostText, i + 1);
     int m = newText.indexOf(middleText);
     if (m != -1) {
       result[i] = newText.substring(0, m);
       result[i + 1] = null;
       calculateMinEditSequence(
           hostText,
           newText.substring(m + middleText.length(), newText.length()),
           result,
           i + 2,
           j);
       return;
     }
     middleText = getRangeText(hostText, j - 1);
     m = newText.lastIndexOf(middleText);
     if (m != -1) {
       result[j] = newText.substring(m + middleText.length());
       result[j - 1] = null;
       calculateMinEditSequence(hostText, newText.substring(0, m), result, i, j - 2);
       return;
     }
     result[i] = "";
     result[j] = "";
     calculateMinEditSequence(hostText, newText, result, i + 1, j - 1);
   }
 }
  private void loadState(Element parentNode) {
    Element versionElement = parentNode.getChild(ELEMENT_VERSION);
    if (versionElement != null) {
      myMajorVersion = versionElement.getAttributeValue(ATTRIBUTE_MAJOR);
      myMinorVersion = versionElement.getAttributeValue(ATTRIBUTE_MINOR);
      myMicroVersion = versionElement.getAttributeValue(ATTRIBUTE_MICRO);
      myPatchVersion = versionElement.getAttributeValue(ATTRIBUTE_PATCH);
      myFullVersionFormat = versionElement.getAttributeValue(ATTRIBUTE_FULL);
      myCodeName = versionElement.getAttributeValue(ATTRIBUTE_CODENAME);
      myEAP = Boolean.parseBoolean(versionElement.getAttributeValue(ATTRIBUTE_EAP));
    }

    Element companyElement = parentNode.getChild(ELEMENT_COMPANY);
    if (companyElement != null) {
      myCompanyName = companyElement.getAttributeValue(ATTRIBUTE_NAME, myCompanyName);
      myShortCompanyName =
          companyElement.getAttributeValue("shortName", shortenCompanyName(myCompanyName));
      myCompanyUrl = companyElement.getAttributeValue(ATTRIBUTE_URL, myCompanyUrl);
    }

    Element buildElement = parentNode.getChild(ELEMENT_BUILD);
    if (buildElement != null) {
      myBuildNumber = buildElement.getAttributeValue(ATTRIBUTE_NUMBER);
      myApiVersion = buildElement.getAttributeValue(ATTRIBUTE_API_VERSION);
      setBuildNumber(myApiVersion, myBuildNumber);

      String dateString = buildElement.getAttributeValue(ATTRIBUTE_DATE);
      if (dateString.equals("__BUILD_DATE__")) {
        myBuildDate = new GregorianCalendar();
        try {
          final JarFile bootJar =
              new JarFile(
                  PathManager.getHomePath() + File.separator + "lib" + File.separator + "boot.jar");
          try {
            final JarEntry jarEntry =
                bootJar.entries().nextElement(); // /META-INF is always updated on build
            myBuildDate.setTime(new Date(jarEntry.getTime()));
          } finally {
            bootJar.close();
          }
        } catch (Exception ignore) {
        }
      } else {
        myBuildDate = parseDate(dateString);
      }
      String majorReleaseDateString = buildElement.getAttributeValue(ATTRIBUTE_MAJOR_RELEASE_DATE);
      if (majorReleaseDateString != null) {
        myMajorReleaseBuildDate = parseDate(majorReleaseDateString);
      }
    }

    Thread currentThread = Thread.currentThread();
    currentThread.setName(
        currentThread.getName()
            + " "
            + myMajorVersion
            + "."
            + myMinorVersion
            + "#"
            + myBuildNumber
            + " "
            + ApplicationNamesInfo.getInstance().getProductName()
            + ", eap:"
            + myEAP
            + ", os:"
            + SystemInfoRt.OS_NAME
            + " "
            + SystemInfoRt.OS_VERSION
            + ", java-version:"
            + SystemProperties.getJavaVendor()
            + " "
            + SystemInfo.JAVA_RUNTIME_VERSION);

    Element logoElement = parentNode.getChild(ELEMENT_LOGO);
    if (logoElement != null) {
      mySplashImageUrl = logoElement.getAttributeValue(ATTRIBUTE_URL);
      mySplashTextColor = parseColor(logoElement.getAttributeValue(ATTRIBUTE_TEXT_COLOR));
      String v = logoElement.getAttributeValue(ATTRIBUTE_PROGRESS_COLOR);
      if (v != null) {
        myProgressColor = parseColor(v);
      }

      v = logoElement.getAttributeValue(ATTRIBUTE_PROGRESS_TAIL_ICON);
      if (v != null) {
        myProgressTailIconName = v;
      }

      v = logoElement.getAttributeValue(ATTRIBUTE_PROGRESS_HEIGHT);
      if (v != null) {
        myProgressHeight = Integer.parseInt(v);
      }

      v = logoElement.getAttributeValue(ATTRIBUTE_PROGRESS_X);
      if (v != null) {
        myProgressX = Integer.parseInt(v);
      }

      v = logoElement.getAttributeValue(ATTRIBUTE_PROGRESS_Y);
      if (v != null) {
        myProgressY = Integer.parseInt(v);
      }

      v = logoElement.getAttributeValue(ATTRIBUTE_LICENSE_TEXT_OFFSET_Y);
      if (v != null) {
        myLicenseOffsetY = Integer.parseInt(v);
      }
    }

    Element aboutLogoElement = parentNode.getChild(ELEMENT_ABOUT);
    if (aboutLogoElement != null) {
      myAboutImageUrl = aboutLogoElement.getAttributeValue(ATTRIBUTE_URL);

      String v = aboutLogoElement.getAttributeValue(ATTRIBUTE_ABOUT_FOREGROUND_COLOR);
      if (v != null) {
        myAboutForeground = parseColor(v);
      }
      v = aboutLogoElement.getAttributeValue(ATTRIBUTE_ABOUT_COPYRIGHT_FOREGROUND_COLOR);
      if (v != null) {
        myCopyrightForeground = parseColor(v);
      }

      String c = aboutLogoElement.getAttributeValue(ATTRIBUTE_ABOUT_LINK_COLOR);
      if (c != null) {
        myAboutLinkColor = parseColor(c);
      }

      String logoX = aboutLogoElement.getAttributeValue("logoX");
      String logoY = aboutLogoElement.getAttributeValue("logoY");
      String logoW = aboutLogoElement.getAttributeValue("logoW");
      String logoH = aboutLogoElement.getAttributeValue("logoH");
      if (logoX != null && logoY != null && logoW != null && logoH != null) {
        try {
          myAboutLogoRect =
              new Rectangle(
                  Integer.parseInt(logoX),
                  Integer.parseInt(logoY),
                  Integer.parseInt(logoW),
                  Integer.parseInt(logoH));
        } catch (NumberFormatException nfe) {
          // ignore
        }
      }
    }

    Element iconElement = parentNode.getChild(ELEMENT_ICON);
    if (iconElement != null) {
      myIconUrl = iconElement.getAttributeValue(ATTRIBUTE_SIZE32);
      mySmallIconUrl = iconElement.getAttributeValue(ATTRIBUTE_SIZE16);
      myBigIconUrl = iconElement.getAttributeValue(ATTRIBUTE_SIZE128, (String) null);
      final String toolWindowIcon = iconElement.getAttributeValue(ATTRIBUTE_SIZE12);
      if (toolWindowIcon != null) {
        myToolWindowIconUrl = toolWindowIcon;
      }
    }

    Element packageElement = parentNode.getChild(ELEMENT_PACKAGE);
    if (packageElement != null) {
      myPackageCode = packageElement.getAttributeValue(ATTRIBUTE_CODE);
    }

    Element showLicensee = parentNode.getChild(ELEMENT_LICENSEE);
    if (showLicensee != null) {
      myShowLicensee =
          Boolean.valueOf(showLicensee.getAttributeValue(ATTRIBUTE_SHOW)).booleanValue();
    }

    Element welcomeScreen = parentNode.getChild(WELCOME_SCREEN_ELEMENT_NAME);
    if (welcomeScreen != null) {
      myWelcomeScreenLogoUrl = welcomeScreen.getAttributeValue(LOGO_URL_ATTR);
    }

    Element wizardSteps = parentNode.getChild(CUSTOMIZE_IDE_WIZARD_STEPS);
    if (wizardSteps != null) {
      myCustomizeIDEWizardStepsProvider = wizardSteps.getAttributeValue(STEPS_PROVIDER);
    }

    Element editor = parentNode.getChild(ELEMENT_EDITOR);
    if (editor != null) {
      myEditorBackgroundImageUrl = editor.getAttributeValue(BACKGROUND_URL_ATTR);
    }

    Element helpElement = parentNode.getChild(HELP_ELEMENT_NAME);
    if (helpElement != null) {
      myHelpFileName = helpElement.getAttributeValue(ATTRIBUTE_HELP_FILE);
      myHelpRootName = helpElement.getAttributeValue(ATTRIBUTE_HELP_ROOT);
      final String webHelpUrl = helpElement.getAttributeValue(ATTRIBUTE_WEBHELP_URL);
      if (webHelpUrl != null) {
        myWebHelpUrl = webHelpUrl;
      }

      String attValue = helpElement.getAttributeValue(ATTRIBUTE_HAS_HELP);
      myHasHelp = attValue == null || Boolean.parseBoolean(attValue); // Default is true

      attValue = helpElement.getAttributeValue(ATTRIBUTE_HAS_CONTEXT_HELP);
      myHasContextHelp = attValue == null || Boolean.parseBoolean(attValue); // Default is true
    }

    Element updateUrls = parentNode.getChild(UPDATE_URLS_ELEMENT_NAME);
    myUpdateUrls = new UpdateUrlsImpl(updateUrls);

    Element documentationElement = parentNode.getChild(ELEMENT_DOCUMENTATION);
    if (documentationElement != null) {
      myDocumentationUrl = documentationElement.getAttributeValue(ATTRIBUTE_URL);
    }

    Element supportElement = parentNode.getChild(ELEMENT_SUPPORT);
    if (supportElement != null) {
      mySupportUrl = supportElement.getAttributeValue(ATTRIBUTE_URL);
    }

    Element feedbackElement = parentNode.getChild(ELEMENT_FEEDBACK);
    if (feedbackElement != null) {
      myEAPFeedbackUrl = feedbackElement.getAttributeValue(ATTRIBUTE_EAP_URL);
      myReleaseFeedbackUrl = feedbackElement.getAttributeValue(ATTRIBUTE_RELEASE_URL);
    }

    Element whatsnewElement = parentNode.getChild(ELEMENT_WHATSNEW);
    if (whatsnewElement != null) {
      myWhatsNewUrl = whatsnewElement.getAttributeValue(ATTRIBUTE_URL);
    }

    Element pluginsElement = parentNode.getChild(ELEMENT_PLUGINS);
    if (pluginsElement != null) {
      String url = pluginsElement.getAttributeValue(ATTRIBUTE_URL);
      myPluginManagerUrl = url != null ? url : DEFAULT_PLUGINS_HOST;
      boolean closed = StringUtil.endsWith(myPluginManagerUrl, "/");

      String listUrl = pluginsElement.getAttributeValue(ATTRIBUTE_LIST_URL);
      myPluginsListUrl =
          listUrl != null ? listUrl : myPluginManagerUrl + (closed ? "" : "/") + "plugins/list/";

      String channelListUrl = pluginsElement.getAttributeValue(ATTRIBUTE_CHANNEL_LIST_URL);
      myChannelsListUrl =
          channelListUrl != null
              ? channelListUrl
              : myPluginManagerUrl + (closed ? "" : "/") + "channels/list/";

      String downloadUrl = pluginsElement.getAttributeValue(ATTRIBUTE_DOWNLOAD_URL);
      myPluginsDownloadUrl =
          downloadUrl != null
              ? downloadUrl
              : myPluginManagerUrl + (closed ? "" : "/") + "pluginManager/";

      if (!getBuild().isSnapshot()) {
        myBuiltinPluginsUrl = pluginsElement.getAttributeValue(ATTRIBUTE_BUILTIN_URL);
      }
    } else {
      myPluginManagerUrl = DEFAULT_PLUGINS_HOST;
      myPluginsListUrl = DEFAULT_PLUGINS_HOST + "/plugins/list/";
      myChannelsListUrl = DEFAULT_PLUGINS_HOST + "/channels/list/";
      myPluginsDownloadUrl = DEFAULT_PLUGINS_HOST + "/pluginManager/";
    }

    final String pluginsHost = System.getProperty("idea.plugins.host");
    if (pluginsHost != null) {
      myPluginsListUrl = myPluginsListUrl.replace(DEFAULT_PLUGINS_HOST, pluginsHost);
      myChannelsListUrl = myChannelsListUrl.replace(DEFAULT_PLUGINS_HOST, pluginsHost);
      myPluginsDownloadUrl = myPluginsDownloadUrl.replace(DEFAULT_PLUGINS_HOST, pluginsHost);
    }

    Element keymapElement = parentNode.getChild(ELEMENT_KEYMAP);
    if (keymapElement != null) {
      myWinKeymapUrl = keymapElement.getAttributeValue(ATTRIBUTE_WINDOWS_URL);
      myMacKeymapUrl = keymapElement.getAttributeValue(ATTRIBUTE_MAC_URL);
    }

    myPluginChooserPages = new ArrayList<PluginChooserPage>();
    final List children = parentNode.getChildren(PLUGINS_PAGE_ELEMENT_NAME);
    for (Object child : children) {
      myPluginChooserPages.add(new PluginChooserPageImpl((Element) child));
    }

    List<Element> essentialPluginsElements = JDOMUtil.getChildren(parentNode, ESSENTIAL_PLUGIN);
    Collection<String> essentialPluginsIds =
        ContainerUtil.mapNotNull(
            essentialPluginsElements,
            new Function<Element, String>() {
              @Override
              public String fun(Element element) {
                String id = element.getTextTrim();
                return StringUtil.isNotEmpty(id) ? id : null;
              }
            });
    myEssentialPluginsIds = ArrayUtil.toStringArray(essentialPluginsIds);

    Element statisticsElement = parentNode.getChild(ELEMENT_STATISTICS);
    if (statisticsElement != null) {
      myStatisticsSettingsUrl = statisticsElement.getAttributeValue(ATTRIBUTE_STATISTICS_SETTINGS);
      myStatisticsServiceUrl = statisticsElement.getAttributeValue(ATTRIBUTE_STATISTICS_SERVICE);
      myStatisticsServiceKey =
          statisticsElement.getAttributeValue(ATTRIBUTE_STATISTICS_SERVICE_KEY);
    } else {
      myStatisticsSettingsUrl = "https://www.jetbrains.com/idea/statistics/stat-assistant.xml";
      myStatisticsServiceUrl = "https://www.jetbrains.com/idea/statistics/index.jsp";
      myStatisticsServiceKey = null;
    }

    Element thirdPartyElement = parentNode.getChild(ELEMENT_THIRD_PARTY);
    if (thirdPartyElement != null) {
      myThirdPartySoftwareUrl = thirdPartyElement.getAttributeValue(ATTRIBUTE_URL);
    }

    Element tvElement = parentNode.getChild(ELEMENT_JB_TV);
    if (tvElement != null) {
      myJetbrainsTvUrl = tvElement.getAttributeValue(ATTRIBUTE_URL);
    }

    Element evaluationElement = parentNode.getChild(ELEMENT_EVALUATION);
    if (evaluationElement != null) {
      final String url = evaluationElement.getAttributeValue(ATTRIBUTE_EVAL_LICENSE_URL);
      if (url != null && !url.isEmpty()) {
        myEvalLicenseUrl = url.trim();
      }
    }

    Element licensingElement = parentNode.getChild(ELEMENT_LICENSING);
    if (licensingElement != null) {
      final String url = licensingElement.getAttributeValue(ATTRIBUTE_KEY_CONVERSION_URL);
      if (url != null && !url.isEmpty()) {
        myKeyConversionUrl = url.trim();
      }
    }

    Element subscriptionsElement = parentNode.getChild(ELEMENT_SUBSCRIPTIONS);
    if (subscriptionsElement != null) {
      mySubscriptionFormId =
          subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_FORM_ID);
      mySubscriptionNewsKey =
          subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_NEWS_KEY);
      mySubscriptionNewsValue =
          subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_NEWS_VALUE, "yes");
      mySubscriptionTipsKey =
          subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_TIPS_KEY);
      mySubscriptionTipsAvailable =
          Boolean.parseBoolean(
              subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_TIPS_AVAILABLE));
      mySubscriptionAdditionalFormData =
          subscriptionsElement.getAttributeValue(ATTRIBUTE_SUBSCRIPTIONS_ADDITIONAL_FORM_DATA);
    }
  }
  @NotNull
  static List<String> getTasksToRun(Module module) {
    final List<String> result;
    final String externalProjectId = ExternalSystemApiUtil.getExternalProjectId(module);
    if (externalProjectId == null) return ContainerUtil.emptyList();
    final String projectPath = ExternalSystemApiUtil.getExternalProjectPath(module);
    if (projectPath == null) return ContainerUtil.emptyList();
    final ExternalProjectInfo externalProjectInfo =
        ExternalSystemUtil.getExternalProjectInfo(
            module.getProject(), GradleConstants.SYSTEM_ID, projectPath);
    if (externalProjectInfo == null) return ContainerUtil.emptyList();

    if (StringUtil.endsWith(externalProjectId, ":test")
        || StringUtil.endsWith(externalProjectId, ":main")) {
      result = TEST_SOURCE_SET_TASKS;
    } else {
      final DataNode<ModuleData> moduleNode =
          GradleProjectResolverUtil.findModule(
              externalProjectInfo.getExternalProjectStructure(), projectPath);
      if (moduleNode == null) return ContainerUtil.emptyList();
      final String sourceSetId =
          StringUtil.substringAfter(
              externalProjectId, moduleNode.getData().getExternalName() + ':');
      if (sourceSetId == null) return ContainerUtil.emptyList();

      final DataNode<TaskData> taskNode =
          ExternalSystemApiUtil.find(
              moduleNode,
              ProjectKeys.TASK,
              new BooleanFunction<DataNode<TaskData>>() {
                @Override
                public boolean fun(DataNode<TaskData> node) {
                  return GradleCommonClassNames.GRADLE_API_TASKS_TESTING_TEST.equals(
                          node.getData().getType())
                      && StringUtil.startsWith(sourceSetId, node.getData().getName());
                }
              });

      if (taskNode == null) return ContainerUtil.emptyList();
      final String taskName = taskNode.getData().getName();
      result = ContainerUtil.list("clean" + StringUtil.capitalize(taskName), taskName);
    }

    final String path;
    if (!externalProjectId.startsWith(":")) {
      path = ":";
    } else {
      final List<String> pathParts = StringUtil.split(externalProjectId, ":");
      if (!pathParts.isEmpty()) pathParts.remove(pathParts.size() - 1);
      final String join = StringUtil.join(pathParts, ":");
      path = ":" + join + (!join.isEmpty() ? ":" : "");
    }
    return ContainerUtil.map(
        result,
        new Function<String, String>() {
          @Override
          public String fun(String s) {
            return path + s;
          }
        });
  }