private void makeProposalsFromChildren(
     DBRProgressMonitor monitor,
     DBSObject parent,
     @Nullable String startPart,
     List<SQLCompletionProposal> proposals) {
   if (startPart != null) {
     startPart = wordDetector.removeQuotes(startPart).toUpperCase();
     int divPos = startPart.lastIndexOf(editor.getSyntaxManager().getStructSeparator());
     if (divPos != -1) {
       startPart = startPart.substring(divPos + 1);
     }
   }
   try {
     Collection<? extends DBSObject> children = null;
     if (parent instanceof DBSObjectContainer) {
       children = ((DBSObjectContainer) parent).getChildren(monitor);
     } else if (parent instanceof DBSEntity) {
       children = ((DBSEntity) parent).getAttributes(monitor);
     }
     if (children != null && !children.isEmpty()) {
       for (DBSObject child : children) {
         if (startPart != null && !child.getName().toUpperCase().startsWith(startPart)) {
           continue;
         }
         proposals.add(makeProposalsFromObject(monitor, child));
       }
     }
   } catch (DBException e) {
     log.error(e);
   }
 }
  /*
   * Turns the vector into an Array of ICompletionProposal objects
   */
  protected SQLCompletionProposal createCompletionProposal(
      String replaceString,
      String displayString,
      String description,
      @Nullable DBPImage image,
      boolean isObject,
      @Nullable DBPNamedObject object) {
    DBPPreferenceStore store = getPreferences();
    DBPDataSource dataSource = editor.getDataSource();
    if (dataSource != null) {
      if (isObject) {
        // Escape replace string if required
        replaceString = DBUtils.getQuotedIdentifier(dataSource, replaceString);
      }
    }

    // If we have quoted string then ignore pref settings
    boolean quotedString = wordDetector.isQuoted(replaceString);
    final int proposalCase =
        quotedString
            ? SQLPreferenceConstants.PROPOSAL_CASE_DEFAULT
            : store.getInt(SQLPreferenceConstants.PROPOSAL_INSERT_CASE);
    switch (proposalCase) {
      case SQLPreferenceConstants.PROPOSAL_CASE_UPPER:
        replaceString = replaceString.toUpperCase();
        break;
      case SQLPreferenceConstants.PROPOSAL_CASE_LOWER:
        replaceString = replaceString.toLowerCase();
        break;
      default:
        DBPIdentifierCase convertCase =
            quotedString && dataSource instanceof SQLDataSource
                ? ((SQLDataSource) dataSource).getSQLDialect().storesQuotedCase()
                : DBPIdentifierCase.MIXED;
        replaceString = convertCase.transform(replaceString);
        break;
    }

    Image img = image == null ? null : DBeaverIcons.getImage(image);
    return new SQLCompletionProposal(
        editor.getSyntaxManager(),
        displayString,
        replaceString, // replacementString
        wordDetector, // wordDetector
        replaceString.length(), // cursorPosition the position of the cursor following the insert
        // relative to replacementOffset
        img, // image to display
        new ContextInformation(
            img,
            displayString,
            displayString), // the context information associated with this proposal
        description,
        object);
  }
 private void makeProposalsFromAssistant(
     DBRProgressMonitor monitor,
     DBSStructureAssistant assistant,
     @Nullable DBSObjectContainer rootSC,
     String objectName,
     List<SQLCompletionProposal> proposals) {
   try {
     Collection<DBSObjectReference> references =
         assistant.findObjectsByMask(
             monitor,
             rootSC,
             assistant.getAutoCompleteObjectTypes(),
             wordDetector.removeQuotes(objectName) + "%",
             wordDetector.isQuoted(objectName),
             100);
     for (DBSObjectReference reference : references) {
       proposals.add(makeProposalsFromObject(reference, reference.getObjectType().getImage()));
     }
   } catch (DBException e) {
     log.error(e);
   }
 }
  private SQLCompletionProposal makeProposalsFromObject(
      DBPNamedObject object, @Nullable DBPImage objectIcon) {
    String objectName = DBUtils.getObjectFullName(object);

    StringBuilder info = new StringBuilder();
    PropertyCollector collector = new PropertyCollector(object, false);
    collector.collectProperties();
    for (DBPPropertyDescriptor descriptor : collector.getPropertyDescriptors2()) {
      Object propValue = collector.getPropertyValue(descriptor.getId());
      if (propValue == null) {
        continue;
      }
      String propString = propValue.toString();
      info.append("<b>").append(descriptor.getDisplayName()).append(":  </b>");
      info.append(propString);
      info.append("<br>");
    }

    boolean isSingleObject = true;
    String replaceString = null;
    DBPDataSource dataSource = editor.getDataSource();
    if (dataSource != null) {
      // If we replace short name with referenced object
      // and current active schema (catalog) is not this object's container then
      // replace with full qualified name
      if (!getPreferences().getBoolean(SQLPreferenceConstants.PROPOSAL_SHORT_NAME)
          && object instanceof DBSObjectReference) {
        if (wordDetector.getFullWord().indexOf(editor.getSyntaxManager().getStructSeparator())
            == -1) {
          DBSObjectReference structObject = (DBSObjectReference) object;
          if (structObject.getContainer() != null) {
            DBSObject selectedObject = getSelectedObject(dataSource);
            if (selectedObject != structObject.getContainer()) {
              replaceString =
                  DBUtils.getFullQualifiedName(dataSource, structObject.getContainer(), object);
              isSingleObject = false;
            }
          }
        }
      }
      if (replaceString == null) {
        replaceString = DBUtils.getQuotedIdentifier(dataSource, object.getName());
      }
    } else {
      replaceString = DBUtils.getObjectShortName(object);
    }
    return createCompletionProposal(
        replaceString, objectName, info.toString(), objectIcon, isSingleObject, object);
  }
 @NotNull
 private ICompletionProposal[] makeTemplateProposals(
     ITextViewer viewer, int documentOffset, String wordPart) {
   wordPart = wordPart.toLowerCase();
   final List<SQLTemplateCompletionProposal> templateProposals = new ArrayList<>();
   // Templates
   for (Template template : editor.getTemplatesPage().getTemplateStore().getTemplates()) {
     if (template.getName().toLowerCase().startsWith(wordPart)) {
       templateProposals.add(
           new SQLTemplateCompletionProposal(
               template,
               new SQLContext(
                   SQLTemplatesRegistry.getInstance()
                       .getTemplateContextRegistry()
                       .getContextType(template.getContextTypeId()),
                   viewer.getDocument(),
                   new Position(wordDetector.getStartOffset(), wordDetector.getLength()),
                   editor),
               new Region(documentOffset, 0),
               null));
     }
   }
   return templateProposals.toArray(new ICompletionProposal[templateProposals.size()]);
 }
  @Nullable
  private DBSObject getTableFromAlias(
      DBRProgressMonitor monitor, DBSObjectContainer sc, @Nullable String token) {
    final DBPDataSource dataSource = editor.getDataSource();
    if (!(dataSource instanceof SQLDataSource)) {
      return null;
    }
    if (activeQuery == null) {
      activeQuery = editor.extractQueryAtPos(documentOffset).getQuery() + " ";
    }

    final List<String> nameList = new ArrayList<>();
    if (token == null) {
      token = "";
    }

    {
      Matcher matcher;
      Pattern aliasPattern;
      SQLDialect sqlDialect = ((SQLDataSource) dataSource).getSQLDialect();
      String quoteString = sqlDialect.getIdentifierQuoteString();
      String quote =
          quoteString == null
              ? SQLConstants.STR_QUOTE_DOUBLE
              : SQLConstants.STR_QUOTE_DOUBLE.equals(quoteString)
                  ? quoteString
                  : Pattern.quote(quoteString);
      String catalogSeparator = sqlDialect.getCatalogSeparator();
      while (token.endsWith(catalogSeparator)) token = token.substring(0, token.length() - 1);

      String tableNamePattern =
          "((?:"
              + quote
              + "(?:[.[^"
              + quote
              + "]]+)"
              + quote
              + ")|(?:[\\w"
              + Pattern.quote(catalogSeparator)
              + "]+))";
      String structNamePattern;
      if (CommonUtils.isEmpty(token)) {
        structNamePattern = "(?:from|update|join|into)\\s*" + tableNamePattern;
      } else {
        structNamePattern =
            tableNamePattern
                + "(?:\\s*\\.\\s*"
                + tableNamePattern
                + ")?"
                + "\\s+(?:(?:AS)\\s)?"
                + token
                + "[\\s,]+";
      }

      try {
        aliasPattern = Pattern.compile(structNamePattern, Pattern.CASE_INSENSITIVE);
      } catch (PatternSyntaxException e) {
        // Bad pattern - seems to be a bad token
        return null;
      }
      matcher = aliasPattern.matcher(activeQuery);
      if (!matcher.find()) {
        return null;
      }

      int groupCount = matcher.groupCount();
      for (int i = 1; i <= groupCount; i++) {
        String group = matcher.group(i);
        if (!CommonUtils.isEmpty(group)) {
          String[] allNames = group.split(Pattern.quote(catalogSeparator));
          for (String name : allNames) {
            if (quoteString != null && name.startsWith(quoteString) && name.endsWith(quoteString)) {
              name = name.substring(1, name.length() - 1);
            }
            nameList.add(name);
          }
        }
      }
    }

    if (nameList.isEmpty()) {
      return null;
    }

    for (int i = 0; i < nameList.size(); i++) {
      nameList.set(
          i, DBObjectNameCaseTransformer.transformName(sc.getDataSource(), nameList.get(i)));
    }

    try {
      DBSObject childObject = null;
      while (childObject == null) {
        childObject = DBUtils.findNestedObject(monitor, sc, nameList);
        if (childObject == null) {
          DBSObjectContainer parentSc = DBUtils.getParentAdapter(DBSObjectContainer.class, sc);
          if (parentSc == null) {
            break;
          }
          sc = parentSc;
        }
      }
      if (childObject == null && nameList.size() <= 1) {
        // No such object found - may be it's start of table name
        DBSStructureAssistant structureAssistant =
            DBUtils.getAdapter(DBSStructureAssistant.class, sc);
        if (structureAssistant != null) {
          String objectNameMask = nameList.get(0);
          Collection<DBSObjectReference> tables =
              structureAssistant.findObjectsByMask(
                  monitor,
                  sc,
                  structureAssistant.getAutoCompleteObjectTypes(),
                  wordDetector.removeQuotes(objectNameMask),
                  wordDetector.isQuoted(objectNameMask),
                  2);
          if (!tables.isEmpty()) {
            return tables.iterator().next().resolveObject(monitor);
          }
        }
        return null;
      } else {
        return childObject;
      }
    } catch (DBException e) {
      log.error(e);
      return null;
    }
  }
  private void makeStructureProposals(
      DBRProgressMonitor monitor, DBPDataSource dataSource, List<SQLCompletionProposal> proposals) {
    final DBSObjectContainer rootContainer =
        DBUtils.getAdapter(DBSObjectContainer.class, dataSource);
    if (rootContainer == null) {
      return;
    }
    DBSObjectContainer selectedContainer = null;
    {
      DBSObject selectedObject = getSelectedObject(dataSource);
      if (selectedObject != null) {
        selectedContainer = DBUtils.getAdapter(DBSObjectContainer.class, selectedObject);
      }
    }

    DBSObjectContainer sc = rootContainer;
    DBSObject childObject = sc;
    List<String> tokens = wordDetector.splitWordPart();

    String lastToken = null;
    for (int i = 0; i < tokens.size(); i++) {
      String token = tokens.get(i);
      if (i == tokens.size() - 1 && !wordDetector.getWordPart().endsWith(".")) {
        lastToken = token;
        break;
      }
      if (sc == null) {
        break;
      }
      // Get next structure container
      try {
        String objectName = DBObjectNameCaseTransformer.transformName(dataSource, token);
        childObject = sc.getChild(monitor, objectName);
        if (childObject == null && i == 0 && selectedContainer != null) {
          // Probably it is from selected object, let's try it
          childObject = selectedContainer.getChild(monitor, objectName);
          if (childObject != null) {
            sc = selectedContainer;
          }
        }
        if (childObject == null) {
          if (i == 0) {
            // Assume it's a table alias ?
            childObject = this.getTableFromAlias(monitor, sc, token);
            if (childObject == null) {
              DBSStructureAssistant structureAssistant =
                  DBUtils.getAdapter(DBSStructureAssistant.class, sc);
              if (structureAssistant != null) {
                Collection<DBSObjectReference> references =
                    structureAssistant.findObjectsByMask(
                        monitor,
                        null,
                        structureAssistant.getAutoCompleteObjectTypes(),
                        wordDetector.removeQuotes(token),
                        wordDetector.isQuoted(token),
                        2);
                if (!references.isEmpty()) {
                  childObject = references.iterator().next().resolveObject(monitor);
                }
              }
            }
          } else {
            // Path element not found. Damn - can't do anything.
            return;
          }
        }

        if (childObject instanceof DBSObjectContainer) {
          sc = (DBSObjectContainer) childObject;
        } else {
          sc = null;
        }
      } catch (DBException e) {
        log.error(e);
        return;
      }
    }
    if (childObject == null) {
      return;
    }
    if (lastToken == null) {
      // Get all children objects as proposals
      makeProposalsFromChildren(monitor, childObject, null, proposals);
    } else {
      // Get matched children
      makeProposalsFromChildren(monitor, childObject, lastToken, proposals);
      if (proposals.isEmpty() || tokens.size() == 1) {
        // At last - try to find child tables by pattern
        DBSStructureAssistant structureAssistant = null;
        for (DBSObject object = childObject; object != null; object = object.getParentObject()) {
          structureAssistant = DBUtils.getAdapter(DBSStructureAssistant.class, object);
          if (structureAssistant != null) {
            break;
          }
        }
        if (structureAssistant != null) {
          makeProposalsFromAssistant(monitor, structureAssistant, sc, lastToken, proposals);
        }
      }
    }
  }
  /**
   * This method returns a list of completion proposals as ICompletionProposal objects. The
   * proposals are based on the word at the offset in the document where the cursor is positioned.
   * In this implementation, we find the word at the document offset and compare it to our list of
   * SQL reserved words. The list is a subset, of those words that match what the user has entered.
   * For example, the text or proposes the SQL keywords OR and ORDER. The list is returned as an
   * array of completion proposals.
   *
   * @see
   *     org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(ITextViewer,
   *     int)
   */
  @Override
  public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int documentOffset) {
    this.documentOffset = documentOffset;
    this.activeQuery = null;

    this.wordDetector =
        new SQLWordPartDetector(viewer.getDocument(), editor.getSyntaxManager(), documentOffset);
    final String wordPart = wordDetector.getWordPart();

    if (lookupTemplates) {
      return makeTemplateProposals(viewer, documentOffset, wordPart);
    }

    final List<SQLCompletionProposal> proposals = new ArrayList<>();
    QueryType queryType = null;
    {
      final String prevKeyWord = wordDetector.getPrevKeyWord();
      if (!CommonUtils.isEmpty(prevKeyWord)) {
        if (editor.getSyntaxManager().getDialect().isEntityQueryWord(prevKeyWord)) {
          queryType = QueryType.TABLE;
        } else if (editor.getSyntaxManager().getDialect().isAttributeQueryWord(prevKeyWord)) {
          queryType = QueryType.COLUMN;
        }
      }
    }
    if (queryType != null) {
      if (editor.getDataSource() != null) {
        try {
          final QueryType qt = queryType;
          DBeaverUI.runInProgressService(
              new DBRRunnableWithProgress() {
                @Override
                public void run(DBRProgressMonitor monitor)
                    throws InvocationTargetException, InterruptedException {
                  monitor.beginTask("Seeking for completion proposals", 1);
                  try {
                    monitor.subTask("Make structure proposals");
                    makeStructureProposals(monitor, proposals, wordPart, qt);
                  } finally {
                    monitor.done();
                  }
                }
              });
        } catch (InvocationTargetException e) {
          log.warn("Error while seeking for structure proposals", e.getTargetException());
        } catch (InterruptedException e) {
          // interrupted - do nothing
        }
      }
    }

    if (proposals.isEmpty() || !wordPart.isEmpty()) {
      // Keyword assist
      List<String> matchedKeywords =
          editor.getSyntaxManager().getDialect().getMatchedKeywords(wordPart);
      for (String keyWord : matchedKeywords) {
        DBPKeywordType keywordType = editor.getSyntaxManager().getDialect().getKeywordType(keyWord);
        if (keywordType != null) {
          proposals.add(
              createCompletionProposal(
                  keyWord, keyWord, keyWord + " (" + keywordType.name() + ")", null, false, null));
        }
      }
    }

    // Remove duplications
    for (int i = 0; i < proposals.size(); i++) {
      SQLCompletionProposal proposal = proposals.get(i);
      for (int j = i + 1; j < proposals.size(); ) {
        SQLCompletionProposal proposal2 = proposals.get(j);
        if (proposal.getDisplayString().equals(proposal2.getDisplayString())) {
          proposals.remove(j);
        } else {
          j++;
        }
      }
    }
    DBSObject selectedObject = getSelectedObject(editor.getDataSource());
    boolean hideDups =
        getPreferences().getBoolean(SQLPreferenceConstants.HIDE_DUPLICATE_PROPOSALS)
            && selectedObject != null;
    if (hideDups) {
      for (int i = 0; i < proposals.size(); i++) {
        SQLCompletionProposal proposal = proposals.get(i);
        for (int j = 0; j < proposals.size(); ) {
          SQLCompletionProposal proposal2 = proposals.get(j);
          if (i != j
              && proposal.hasStructObject()
              && proposal2.hasStructObject()
              && CommonUtils.equalObjects(
                  proposal.getObject().getName(), proposal2.getObject().getName())
              && proposal.getObjectContainer() == selectedObject) {
            proposals.remove(j);
          } else {
            j++;
          }
        }
      }
    }

    if (hideDups) {
      // Remove duplicates from non-active schema

      if (selectedObject instanceof DBSObjectContainer) {
        // List<ICompletionProposal>
      }
    }
    return proposals.toArray(new ICompletionProposal[proposals.size()]);
  }