private Long getQNameId(QName qname) {
   Pair<Long, QName> qnamePair = qnameDAO.getQName(qname);
   if (qnamePair == null) {
     throw new AlfrescoRuntimeException("QName does not exist: " + qname);
   }
   return qnamePair.getFirst();
 }
  public CannedQuery<AuthorityInfo> getCannedQuery(
      AuthorityType type,
      NodeRef containerRef,
      String displayNameFilter,
      boolean sortByDisplayName,
      boolean sortAscending,
      PagingRequest pagingRequest) {
    ParameterCheck.mandatory("containerRef", containerRef);
    ParameterCheck.mandatory("pagingRequest", pagingRequest);

    int requestTotalCountMax = pagingRequest.getRequestTotalCountMax();

    Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(tenantService.getName(containerRef));
    if (nodePair == null) {
      throw new InvalidNodeRefException(
          "Container ref does not exist: " + containerRef, containerRef);
    }

    Long containerNodeId = nodePair.getFirst();

    // specific query params
    GetAuthoritiesCannedQueryParams paramBean =
        new GetAuthoritiesCannedQueryParams(
            type,
            containerNodeId,
            getQNameId(ContentModel.PROP_AUTHORITY_DISPLAY_NAME),
            displayNameFilter);

    // page details
    CannedQueryPageDetails cqpd =
        new CannedQueryPageDetails(
            pagingRequest.getSkipCount(),
            pagingRequest.getMaxItems(),
            CannedQueryPageDetails.DEFAULT_PAGE_NUMBER,
            CannedQueryPageDetails.DEFAULT_PAGE_COUNT);

    // sort details
    CannedQuerySortDetails cqsd = null;
    if (sortByDisplayName) {
      List<Pair<? extends Object, SortOrder>> sortPairs =
          new ArrayList<Pair<? extends Object, SortOrder>>(1);
      sortPairs.add(
          new Pair<String, SortOrder>(
              "sortByName",
              (sortAscending
                  ? SortOrder.ASCENDING
                  : SortOrder.DESCENDING))); // note: sortByName is implied
      cqsd = new CannedQuerySortDetails(sortPairs);
    }

    // create query params holder
    CannedQueryParameters params =
        new CannedQueryParameters(
            paramBean, cqpd, cqsd, requestTotalCountMax, pagingRequest.getQueryExecutionId());

    // return canned query instance
    return getCannedQuery(params);
  }
  /**
   * @see org.alfresco.web.action.ActionEvaluator#evaluate(org.alfresco.web.bean.repository.Node)
   */
  public boolean evaluate(final Node node) {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    AVMService avmService = Repository.getServiceRegistry(facesContext).getAVMService();

    Pair<Integer, String> p = AVMNodeConverter.ToAVMVersionPath(node.getNodeRef());
    AVMNodeDescriptor nodeDesc = avmService.lookup(-1, p.getSecond());

    // don't allow action if its a 'layeredfolder' and a primary indirection
    return !(nodeDesc.getType() == AVMNodeType.LAYERED_DIRECTORY && nodeDesc.isPrimary());
  }
  /**
   * Returns the Ticket in the form used for HTTP Basic Authentication. This should be added as the
   * value to a HTTP Request Header with key Authorization
   */
  public String getAsHTTPAuthorization() {
    // Build from the Username and Password
    Pair<String, String> userPass = getAsUsernameAndPassword();
    Credentials credentials =
        new UsernamePasswordCredentials(userPass.getFirst(), userPass.getSecond());

    // Encode it into the required format
    String credentialsEncoded =
        Base64.encodeBytes(credentials.toString().getBytes(utf8), Base64.DONT_BREAK_LINES);

    // Mark it as Basic, and we're done
    return "Basic " + credentialsEncoded;
  }
  /*
   * (non-Javadoc)
   * @see org.alfresco.repo.security.authority.AuthorityBridgeDAO#getAuthorityBridgeLinks()
   */
  @Override
  public List<AuthorityBridgeLink> getAuthorityBridgeLinks() {
    Long authorityContainerTypeQNameId = Long.MIN_VALUE;
    Pair<Long, QName> authorityContainerTypeQNamePair =
        qnameDAO.getQName(ContentModel.TYPE_AUTHORITY_CONTAINER);
    if (authorityContainerTypeQNamePair != null) {
      authorityContainerTypeQNameId = authorityContainerTypeQNamePair.getFirst();
    }

    Long memberAssocQNameId = Long.MIN_VALUE;
    Pair<Long, QName> memberAssocQNamePair = qnameDAO.getQName(ContentModel.ASSOC_MEMBER);
    if (memberAssocQNamePair != null) {
      memberAssocQNameId = memberAssocQNamePair.getFirst();
    }

    Long authorityNameQNameId = Long.MIN_VALUE;
    Pair<Long, QName> authorityNameQNamePair = qnameDAO.getQName(ContentModel.PROP_AUTHORITY_NAME);
    if (authorityNameQNamePair != null) {
      authorityNameQNameId = authorityNameQNamePair.getFirst();
    }

    // Get tenenat specifc store id
    StoreRef tenantSpecificStoreRef =
        tenantService.getName(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
    Long storeId = Long.MIN_VALUE;
    if (tenantSpecificStoreRef != null) {
      Pair<Long, StoreRef> storePair = nodeDAO.getStore(tenantSpecificStoreRef);
      if (storePair != null) {
        storeId = storePair.getFirst();
      }
    }

    return selectAuthorityBridgeLinks(
        authorityContainerTypeQNameId, memberAssocQNameId, authorityNameQNameId, storeId);
  }
 /**
  * Helper method to set the {@link #setTypeQNameId(Long)}.
  *
  * @param qnameDAO the DAO to resolve the QName ID
  * @param typeQName the association type
  * @param forUpdate <tt>true</tt> if the QName must exist i.e. this entity will be used for
  *     updates and the type <code>QName</code> <b>must</b> exist.
  * @return <tt>true</tt> if the set worked otherwise <tt>false</tt>
  */
 public boolean setTypeQNameAll(QNameDAO qnameDAO, QName typeQName, boolean forUpdate) {
   if (forUpdate) {
     typeQNameId = qnameDAO.getOrCreateQName(typeQName).getFirst();
     return true;
   } else {
     Pair<Long, QName> qnamePair = qnameDAO.getQName(typeQName);
     if (qnamePair == null) {
       return false;
     } else {
       typeQNameId = qnamePair.getFirst();
       return true;
     }
   }
 }
  private String getAclTenant(long aclId) {
    List<Long> nodeIds = aclDAO.getADMNodesByAcl(aclId, 1);
    if (nodeIds.size() == 0) {
      return null;
    }

    nodeDAO.setCheckNodeConsistency();
    Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(nodeIds.get(0));
    if (nodePair == null) {
      return null;
    }

    return tenantService.getDomain(nodePair.getSecond().getStoreRef().getIdentifier());
  }
 /** Test Going betwwen a NodeRef and a version, path pair. */
 public void testTranslate() {
   String avmPath = "main:/";
   int version = 2;
   NodeRef nodeRef = AVMNodeConverter.ToNodeRef(version, avmPath);
   System.out.println(nodeRef);
   Pair<Integer, String> backOut = AVMNodeConverter.ToAVMVersionPath(nodeRef);
   assertEquals(2, backOut.getFirst().intValue());
   assertEquals(avmPath, backOut.getSecond());
   avmPath = "main:/fista/mista/wisticuff";
   version = -1;
   nodeRef = AVMNodeConverter.ToNodeRef(version, avmPath);
   System.out.println(nodeRef);
   backOut = AVMNodeConverter.ToAVMVersionPath(nodeRef);
   assertEquals(-1, backOut.getFirst().intValue());
   assertEquals(avmPath, backOut.getSecond());
 }
  /**
   * Helper method to set all values associated with the {@link #setChildNodeName(String) child node
   * name}.
   *
   * @param dictionaryService the service that determines how the CRC values are generated. If this
   *     is <tt>null</tt> then the CRC values are generated assuming that positive enforcement of
   *     the name constraint is required.
   * @param childNodeName the child node name
   */
  public void setChildNodeNameAll(
      DictionaryService dictionaryService, QName typeQName, String childNodeName) {
    ParameterCheck.mandatory("childNodeName", childNodeName);

    if (dictionaryService != null) {
      ParameterCheck.mandatory("typeQName", typeQName);

      Pair<String, Long> childNameUnique =
          ChildAssocEntity.getChildNameUnique(dictionaryService, typeQName, childNodeName);
      this.childNodeName = childNameUnique.getFirst();
      this.childNodeNameCrc = childNameUnique.getSecond();
    } else {
      String childNameNewLower = childNodeName.toLowerCase();
      this.childNodeName = ChildAssocEntity.getChildNodeNameShort(childNameNewLower);
      this.childNodeNameCrc = ChildAssocEntity.getChildNodeNameCrc(childNameNewLower);
    }
  }
  /**
   * Executes a solr query for statistics
   *
   * @param searchParameters StatsParameters
   * @return SolrStatsResult
   */
  public SolrStatsResult executeStatsQuery(final StatsParameters searchParameters) {
    if (repositoryState.isBootstrapping()) {
      throw new AlfrescoRuntimeException(
          "SOLR stats queries can not be executed while the repository is bootstrapping");
    }

    try {
      StoreRef store = extractStoreRef(searchParameters);
      SolrStoreMappingWrapper mapping = extractMapping(store);
      Locale locale = extractLocale(searchParameters);

      Pair<HttpClient, String> httpClientAndBaseUrl = mapping.getHttpClientAndBaseUrl();
      HttpClient httpClient = httpClientAndBaseUrl.getFirst();
      String url = buildStatsUrl(searchParameters, httpClientAndBaseUrl.getSecond(), locale);
      JSONObject body =
          buildStatsBody(searchParameters, tenantService.getCurrentUserDomain(), locale);

      if (httpClient == null) {
        throw new AlfrescoRuntimeException("No http client for store " + store.toString());
      }

      return (SolrStatsResult)
          postSolrQuery(
              httpClient,
              url,
              body,
              new SolrJsonProcessor<SolrStatsResult>() {

                @Override
                public SolrStatsResult getResult(JSONObject json) {
                  return new SolrStatsResult(json, searchParameters.isDateSearch());
                }
              });

    } catch (UnsupportedEncodingException e) {
      throw new LuceneQueryParserException("stats", e);
    } catch (HttpException e) {
      throw new LuceneQueryParserException("stats", e);
    } catch (IOException e) {
      throw new LuceneQueryParserException("stats", e);
    } catch (JSONException e) {
      throw new LuceneQueryParserException("stats", e);
    }
  }
  /**
   * Set all required fields associated with the patch <code>QName</code>.
   *
   * @param forUpdate <tt>true</tt> if the entity is going to be used for a data update i.e. the
   *     <code>QName</code> <b>must</b> exist.
   * @return Returns <tt>true</tt> if the <code>QName</code> namespace exists.
   */
  public boolean setQNameAll(QNameDAO qnameDAO, QName qname, boolean forUpdate) {
    String assocQNameNamespace = qname.getNamespaceURI();
    String assocQNameLocalName = qname.getLocalName();
    Long assocQNameNamespaceId = null;
    if (forUpdate) {
      assocQNameNamespaceId = qnameDAO.getOrCreateNamespace(assocQNameNamespace).getFirst();
    } else {
      Pair<Long, String> nsPair = qnameDAO.getNamespace(assocQNameNamespace);
      if (nsPair == null) {
        // We can't set anything
        return false;
      } else {
        assocQNameNamespaceId = nsPair.getFirst();
      }
    }
    Long assocQNameCrc = getQNameCrc(qname);

    this.qnameNamespaceId = assocQNameNamespaceId;
    this.qnameLocalName = assocQNameLocalName;
    this.qnameCrc = assocQNameCrc;

    // All set correctly
    return true;
  }
  public ResultSet executeQuery(final SearchParameters searchParameters, String language) {
    if (repositoryState.isBootstrapping()) {
      throw new AlfrescoRuntimeException(
          "SOLR queries can not be executed while the repository is bootstrapping");
    }

    try {
      StoreRef store = extractStoreRef(searchParameters);
      SolrStoreMappingWrapper mapping = extractMapping(store);
      Locale locale = extractLocale(searchParameters);

      URLCodec encoder = new URLCodec();
      StringBuilder url = new StringBuilder();

      Pair<HttpClient, String> httpClientAndBaseUrl = mapping.getHttpClientAndBaseUrl();
      HttpClient httpClient = httpClientAndBaseUrl.getFirst();

      url.append(httpClientAndBaseUrl.getSecond());

      String languageUrlFragment = extractLanguageFragment(language);
      url.append("/").append(languageUrlFragment);

      // Send the query in JSON only
      // url.append("?q=");
      // url.append(encoder.encode(searchParameters.getQuery(), "UTF-8"));
      url.append("?wt=").append(encoder.encode("json", "UTF-8"));
      url.append("&fl=").append(encoder.encode("DBID,score", "UTF-8"));

      if ((searchParameters.getStores().size() > 1) || (mapping.isSharded())) {
        boolean requiresSeparator = false;
        url.append("&shards=");
        for (StoreRef storeRef : searchParameters.getStores()) {
          SolrStoreMappingWrapper storeMapping = extractMapping(storeRef);

          if (requiresSeparator) {
            url.append(',');
          } else {
            requiresSeparator = true;
          }

          url.append(storeMapping.getShards());
        }
      }

      // Emulate old limiting behaviour and metadata
      final LimitBy limitBy;
      int maxResults = -1;
      if (searchParameters.getMaxItems() >= 0) {
        maxResults = searchParameters.getMaxItems();
        limitBy = LimitBy.FINAL_SIZE;
      } else if (searchParameters.getLimitBy() == LimitBy.FINAL_SIZE
          && searchParameters.getLimit() >= 0) {
        maxResults = searchParameters.getLimit();
        limitBy = LimitBy.FINAL_SIZE;
      } else {
        maxResults = searchParameters.getMaxPermissionChecks();
        if (maxResults < 0) {
          maxResults = maximumResultsFromUnlimitedQuery;
        }
        limitBy = LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS;
      }
      url.append("&rows=").append(String.valueOf(maxResults));

      url.append("&df=").append(encoder.encode(searchParameters.getDefaultFieldName(), "UTF-8"));
      url.append("&start=").append(encoder.encode("" + searchParameters.getSkipCount(), "UTF-8"));

      url.append("&locale=");
      url.append(encoder.encode(locale.toString(), "UTF-8"));
      url.append("&")
          .append(SearchParameters.ALTERNATIVE_DICTIONARY)
          .append("=")
          .append(alternativeDictionary);
      for (String paramName : searchParameters.getExtraParameters().keySet()) {
        url.append("&")
            .append(paramName)
            .append("=")
            .append(searchParameters.getExtraParameters().get(paramName));
      }
      StringBuffer sortBuffer = buildSortParameters(searchParameters, encoder);
      url.append(sortBuffer);

      if (searchParameters.getPermissionEvaluation() != PermissionEvaluationMode.NONE) {
        url.append("&fq=").append(encoder.encode("{!afts}AUTHORITY_FILTER_FROM_JSON", "UTF-8"));
      }

      if (searchParameters.getExcludeTenantFilter() == false) {
        url.append("&fq=").append(encoder.encode("{!afts}TENANT_FILTER_FROM_JSON", "UTF-8"));
      }

      if (searchParameters.getFieldFacets().size() > 0) {
        url.append("&facet=").append(encoder.encode("true", "UTF-8"));
        for (FieldFacet facet : searchParameters.getFieldFacets()) {
          url.append("&facet.field=").append(encoder.encode(facet.getField(), "UTF-8"));
          if (facet.getEnumMethodCacheMinDF() != 0) {
            url.append("&")
                .append(
                    encoder.encode("f." + facet.getField() + ".facet.enum.cache.minDf", "UTF-8"))
                .append("=")
                .append(encoder.encode("" + facet.getEnumMethodCacheMinDF(), "UTF-8"));
          }
          url.append("&")
              .append(encoder.encode("f." + facet.getField() + ".facet.limit", "UTF-8"))
              .append("=")
              .append(encoder.encode("" + facet.getLimit(), "UTF-8"));
          if (facet.getMethod() != null) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.method", "UTF-8"))
                .append("=")
                .append(
                    encoder.encode(
                        facet.getMethod() == FieldFacetMethod.ENUM ? "enum" : "fc", "UTF-8"));
          }
          if (facet.getMinCount() != 0) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.mincount", "UTF-8"))
                .append("=")
                .append(encoder.encode("" + facet.getMinCount(), "UTF-8"));
          }
          if (facet.getOffset() != 0) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.offset", "UTF-8"))
                .append("=")
                .append(encoder.encode("" + facet.getOffset(), "UTF-8"));
          }
          if (facet.getPrefix() != null) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.prefix", "UTF-8"))
                .append("=")
                .append(encoder.encode("" + facet.getPrefix(), "UTF-8"));
          }
          if (facet.getSort() != null) {
            url.append("&")
                .append(encoder.encode("f." + facet.getField() + ".facet.sort", "UTF-8"))
                .append("=")
                .append(
                    encoder.encode(
                        facet.getSort() == FieldFacetSort.COUNT ? "count" : "index", "UTF-8"));
          }
        }
        for (String facetQuery : searchParameters.getFacetQueries()) {
          url.append("&facet.query=").append(encoder.encode("{!afts}" + facetQuery, "UTF-8"));
        }
      }
      // end of field facets

      final String searchTerm = searchParameters.getSearchTerm();
      String spellCheckQueryStr = null;
      if (searchTerm != null && searchParameters.isSpellCheck()) {
        StringBuilder builder = new StringBuilder();
        builder.append("&spellcheck.q=").append(encoder.encode(searchTerm, "UTF-8"));
        builder.append("&spellcheck=").append(encoder.encode("true", "UTF-8"));
        spellCheckQueryStr = builder.toString();
        url.append(spellCheckQueryStr);
      }

      JSONObject body = new JSONObject();
      body.put("query", searchParameters.getQuery());

      // Authorities go over as is - and tenant mangling and query building takes place on the SOLR
      // side

      Set<String> allAuthorisations = permissionService.getAuthorisations();
      boolean includeGroups =
          includeGroupsForRoleAdmin
              ? true
              : !allAuthorisations.contains(PermissionService.ADMINISTRATOR_AUTHORITY);

      JSONArray authorities = new JSONArray();
      for (String authority : allAuthorisations) {
        if (includeGroups) {
          authorities.put(authority);
        } else {
          if (AuthorityType.getAuthorityType(authority) != AuthorityType.GROUP) {
            authorities.put(authority);
          }
        }
      }
      body.put("authorities", authorities);
      body.put("anyDenyDenies", anyDenyDenies);

      JSONArray tenants = new JSONArray();
      tenants.put(tenantService.getCurrentUserDomain());
      body.put("tenants", tenants);

      JSONArray locales = new JSONArray();
      for (Locale currentLocale : searchParameters.getLocales()) {
        locales.put(DefaultTypeConverter.INSTANCE.convert(String.class, currentLocale));
      }
      if (locales.length() == 0) {
        locales.put(I18NUtil.getLocale());
      }
      body.put("locales", locales);

      JSONArray templates = new JSONArray();
      for (String templateName : searchParameters.getQueryTemplates().keySet()) {
        JSONObject template = new JSONObject();
        template.put("name", templateName);
        template.put("template", searchParameters.getQueryTemplates().get(templateName));
        templates.put(template);
      }
      body.put("templates", templates);

      JSONArray allAttributes = new JSONArray();
      for (String attribute : searchParameters.getAllAttributes()) {
        allAttributes.put(attribute);
      }
      body.put("allAttributes", allAttributes);

      body.put("defaultFTSOperator", searchParameters.getDefaultFTSOperator());
      body.put("defaultFTSFieldOperator", searchParameters.getDefaultFTSFieldOperator());
      body.put("queryConsistency", searchParameters.getQueryConsistency());
      if (searchParameters.getMlAnalaysisMode() != null) {
        body.put("mlAnalaysisMode", searchParameters.getMlAnalaysisMode().toString());
      }
      body.put("defaultNamespace", searchParameters.getNamespace());

      JSONArray textAttributes = new JSONArray();
      for (String attribute : searchParameters.getTextAttributes()) {
        textAttributes.put(attribute);
      }
      body.put("textAttributes", textAttributes);

      final int maximumResults = maxResults; // just needed for the final parameter

      return (ResultSet)
          postSolrQuery(
              httpClient,
              url.toString(),
              body,
              new SolrJsonProcessor<SolrJSONResultSet>() {

                @Override
                public SolrJSONResultSet getResult(JSONObject json) {
                  return new SolrJSONResultSet(
                      json, searchParameters, nodeService, nodeDAO, limitBy, maximumResults);
                }
              },
              spellCheckQueryStr);
    } catch (UnsupportedEncodingException e) {
      throw new LuceneQueryParserException("", e);
    } catch (HttpException e) {
      throw new LuceneQueryParserException("", e);
    } catch (IOException e) {
      throw new LuceneQueryParserException("", e);
    } catch (JSONException e) {
      throw new LuceneQueryParserException("", e);
    }
  }
 /** Public constructor, but not generally useful */
 public NodeParametersEntity(QNameDAO qnameDAO) {
   Pair<Long, QName> qnamePair = qnameDAO.getQName(ContentModel.PROP_ORIGINAL_ID);
   this.setOriginalIdPropQNameId(qnamePair == null ? -1 : qnamePair.getFirst());
 }
  public List<Pair<String, Integer>> getTopTerms(String field, int count) {
    ClosingIndexSearcher searcher = null;
    try {
      LinkedList<Pair<String, Integer>> answer = new LinkedList<Pair<String, Integer>>();
      searcher = getSearcher(indexer);
      IndexReader reader = searcher.getIndexReader();
      TermEnum terms = reader.terms(new Term(field, ""));
      do {
        Term term = terms.term();
        if (term != null) {
          if (!term.field().equals(field)) {
            break;
          }
          int freq = terms.docFreq();
          Pair<String, Integer> pair =
              new Pair<String, Integer>(term.text(), Integer.valueOf(freq));
          if (answer.size() < count) {
            if (answer.size() == 0) {
              answer.add(pair);
            } else if (answer.get(answer.size() - 1).getSecond().compareTo(pair.getSecond()) >= 0) {
              answer.add(pair);
            } else {
              for (ListIterator<Pair<String, Integer>> it = answer.listIterator();
                  it.hasNext(); /**/ ) {
                Pair<String, Integer> test = it.next();
                if (test.getSecond().compareTo(pair.getSecond()) < 0) {
                  it.previous();
                  it.add(pair);
                  break;
                }
              }
            }
          } else if (answer.get(count - 1).getSecond().compareTo(pair.getSecond()) < 0) {
            for (ListIterator<Pair<String, Integer>> it = answer.listIterator();
                it.hasNext(); /**/ ) {
              Pair<String, Integer> test = it.next();
              if (test.getSecond().compareTo(pair.getSecond()) < 0) {
                it.previous();
                it.add(pair);
                break;
              }
            }
            answer.removeLast();
          } else {
            // off the end
          }
        }
      } while (terms.next());
      terms.close();
      return answer;

    } catch (IOException e) {
      throw new SearcherException(e);
    } finally {
      if (searcher != null) {
        try {
          searcher.close();
        } catch (IOException e) {
          throw new SearcherException(e);
        }
      }
    }
  }
  /** {@inheritDoc} */
  public void getNodesMetadata(
      NodeMetaDataParameters nodeMetaDataParameters,
      MetaDataResultsFilter resultFilter,
      NodeMetaDataQueryCallback callback) {
    if (false == enabled) {
      return;
    }

    NodeMetaDataQueryRowHandler rowHandler = new NodeMetaDataQueryRowHandler(callback);
    boolean includeType = (resultFilter == null ? true : resultFilter.getIncludeType());
    boolean includeProperties = (resultFilter == null ? true : resultFilter.getIncludeProperties());
    boolean includeAspects = (resultFilter == null ? true : resultFilter.getIncludeAspects());
    boolean includePaths = (resultFilter == null ? true : resultFilter.getIncludePaths());
    boolean includeNodeRef = (resultFilter == null ? true : resultFilter.getIncludeNodeRef());
    boolean includeParentAssociations =
        (resultFilter == null ? true : resultFilter.getIncludeParentAssociations());
    boolean includeChildAssociations =
        (resultFilter == null ? true : resultFilter.getIncludeChildAssociations());
    boolean includeOwner = (resultFilter == null ? true : resultFilter.getIncludeOwner());
    boolean includeChildIds = (resultFilter == null ? true : resultFilter.getIncludeChildIds());
    boolean includeTxnId = (resultFilter == null ? true : resultFilter.getIncludeTxnId());

    List<Long> nodeIds = preCacheNodes(nodeMetaDataParameters);

    for (Long nodeId : nodeIds) {
      Status status = nodeDAO.getNodeIdStatus(nodeId);
      if (status == null) {
        // We've been called with the ID of a purged node, probably due to processing a transaction
        // with a
        // cascading delete. Fine to skip and assume it will be processed in a transaction.
        // See org.alfresco.solr.tracker.CoreTracker.updateDescendantAuxDocs(NodeMetaData, boolean,
        // SolrIndexSearcher)
        continue;
      }
      NodeRef nodeRef = status.getNodeRef();

      NodeMetaData nodeMetaData = new NodeMetaData();
      nodeMetaData.setNodeId(nodeId);

      if (includeNodeRef) {
        nodeMetaData.setNodeRef(tenantService.getBaseName(nodeRef, true));
      }

      if (includeTxnId) {
        nodeMetaData.setTxnId(status.getDbTxnId());
      }

      if (status.isDeleted()) {
        rowHandler.processResult(nodeMetaData);
        continue;
      }

      Map<QName, Serializable> props = null;
      Set<QName> aspects = null;

      nodeMetaData.setAclId(nodeDAO.getNodeAclId(nodeId));

      if (includeType) {
        QName nodeType = getNodeType(nodeId);
        if (nodeType != null) {
          nodeMetaData.setNodeType(nodeType);
        } else {
          throw new AlfrescoRuntimeException("Nodes with no type are ignored by SOLR");
        }
      }

      if (includeProperties) {
        if (props == null) {
          props = getProperties(nodeId);
        }
        nodeMetaData.setProperties(props);
      } else {
        nodeMetaData.setProperties(Collections.<QName, Serializable>emptyMap());
      }

      if (includeAspects || includePaths || includeParentAssociations) {
        aspects = getNodeAspects(nodeId);
      }
      nodeMetaData.setAspects(aspects);

      boolean ignoreLargeMetadata =
          (typeIndexFilter.shouldBeIgnored(getNodeType(nodeId))
              || aspectIndexFilter.shouldBeIgnored(getNodeAspects(nodeId)));
      if (!ignoreLargeMetadata
          && (typeIndexFilter.isIgnorePathsForSpecificTypes()
              || aspectIndexFilter.isIgnorePathsForSpecificAspects())) {
        // check if parent should be ignored
        final List<Long> parentIds = new LinkedList<Long>();
        nodeDAO.getParentAssocs(
            nodeId,
            null,
            null,
            true,
            new ChildAssocRefQueryCallback() {
              @Override
              public boolean preLoadNodes() {
                return false;
              }

              @Override
              public boolean orderResults() {
                return false;
              }

              @Override
              public boolean handle(
                  Pair<Long, ChildAssociationRef> childAssocPair,
                  Pair<Long, NodeRef> parentNodePair,
                  Pair<Long, NodeRef> childNodePair) {
                parentIds.add(parentNodePair.getFirst());
                return false;
              }

              @Override
              public void done() {}
            });

        if (!parentIds.isEmpty()) {
          Long parentId = parentIds.iterator().next();
          if (typeIndexFilter.isIgnorePathsForSpecificTypes()) {
            QName parentType = getNodeType(parentId);
            ignoreLargeMetadata = typeIndexFilter.shouldBeIgnored(parentType);
          }
          if (!ignoreLargeMetadata && aspectIndexFilter.isIgnorePathsForSpecificAspects()) {
            ignoreLargeMetadata = aspectIndexFilter.shouldBeIgnored(getNodeAspects(parentId));
          }
        }
      }

      CategoryPaths categoryPaths =
          new CategoryPaths(
              new ArrayList<Pair<Path, QName>>(), new ArrayList<ChildAssociationRef>());
      if (!ignoreLargeMetadata && (includePaths || includeParentAssociations)) {
        if (props == null) {
          props = getProperties(nodeId);
        }
        categoryPaths = getCategoryPaths(status.getNodeRef(), aspects, props);
      }

      if (includePaths && !ignoreLargeMetadata) {
        if (props == null) {
          props = getProperties(nodeId);
        }

        List<Path> directPaths =
            nodeDAO.getPaths(new Pair<Long, NodeRef>(nodeId, status.getNodeRef()), false);

        Collection<Pair<Path, QName>> paths =
            new ArrayList<Pair<Path, QName>>(directPaths.size() + categoryPaths.getPaths().size());
        for (Path path : directPaths) {
          paths.add(new Pair<Path, QName>(path.getBaseNamePath(tenantService), null));
        }
        for (Pair<Path, QName> catPair : categoryPaths.getPaths()) {
          paths.add(
              new Pair<Path, QName>(
                  catPair.getFirst().getBaseNamePath(tenantService), catPair.getSecond()));
        }

        nodeMetaData.setPaths(paths);

        // Calculate name path
        Collection<Collection<String>> namePaths = new ArrayList<Collection<String>>(2);
        nodeMetaData.setNamePaths(namePaths);
        for (Pair<Path, QName> catPair : paths) {
          Path path = catPair.getFirst();

          boolean added = false;
          List<String> namePath = new ArrayList<String>(path.size());
          NEXT_ELEMENT:
          for (Path.Element pathElement : path) {
            if (!(pathElement instanceof ChildAssocElement)) {
              // This is some path element that is terminal to a cm:name path
              break;
            }
            ChildAssocElement pathChildAssocElement = (ChildAssocElement) pathElement;
            NodeRef childNodeRef = pathChildAssocElement.getRef().getChildRef();
            Pair<Long, NodeRef> childNodePair = nodeDAO.getNodePair(childNodeRef);
            if (childNodePair == null) {
              // Gone
              break;
            }
            Long childNodeId = childNodePair.getFirst();
            String childNodeName =
                (String) nodeDAO.getNodeProperty(childNodeId, ContentModel.PROP_NAME);
            if (childNodeName == null) {
              // We have hit a non-name node, which acts as a root for cm:name
              // DH: There is no particular constraint here.  This is just a decision made.
              namePath.clear();
              // We have to continue down the path as there could be a name path lower down
              continue NEXT_ELEMENT;
            }
            // We can finally add the name to the path
            namePath.add(childNodeName);
            // Add the path if this is the first entry in the name path
            if (!added) {
              namePaths.add(namePath);
              added = true;
            }
          }
        }
      }

      nodeMetaData.setTenantDomain(tenantService.getDomain(nodeRef.getStoreRef().getIdentifier()));

      if (includeChildAssociations) {
        final List<ChildAssociationRef> childAssocs = new ArrayList<ChildAssociationRef>(100);
        nodeDAO.getChildAssocs(
            nodeId,
            null,
            null,
            null,
            null,
            null,
            new ChildAssocRefQueryCallback() {
              @Override
              public boolean preLoadNodes() {
                return false;
              }

              @Override
              public boolean orderResults() {
                return false;
              }

              @Override
              public boolean handle(
                  Pair<Long, ChildAssociationRef> childAssocPair,
                  Pair<Long, NodeRef> parentNodePair,
                  Pair<Long, NodeRef> childNodePair) {
                boolean addCurrentChildAssoc = true;
                if (typeIndexFilter.isIgnorePathsForSpecificTypes()) {
                  QName nodeType = nodeDAO.getNodeType(childNodePair.getFirst());
                  addCurrentChildAssoc = !typeIndexFilter.shouldBeIgnored(nodeType);
                }
                if (!addCurrentChildAssoc && aspectIndexFilter.isIgnorePathsForSpecificAspects()) {
                  addCurrentChildAssoc =
                      !aspectIndexFilter.shouldBeIgnored(getNodeAspects(childNodePair.getFirst()));
                }
                if (addCurrentChildAssoc) {
                  childAssocs.add(tenantService.getBaseName(childAssocPair.getSecond(), true));
                }
                return true;
              }

              @Override
              public void done() {}
            });
        nodeMetaData.setChildAssocs(childAssocs);
      }

      if (includeChildIds) {
        final List<Long> childIds = new ArrayList<Long>(100);
        nodeDAO.getChildAssocs(
            nodeId,
            null,
            null,
            null,
            null,
            null,
            new ChildAssocRefQueryCallback() {
              @Override
              public boolean preLoadNodes() {
                return false;
              }

              @Override
              public boolean orderResults() {
                return false;
              }

              @Override
              public boolean handle(
                  Pair<Long, ChildAssociationRef> childAssocPair,
                  Pair<Long, NodeRef> parentNodePair,
                  Pair<Long, NodeRef> childNodePair) {
                boolean addCurrentId = true;
                if (typeIndexFilter.isIgnorePathsForSpecificTypes()) {
                  QName nodeType = nodeDAO.getNodeType(childNodePair.getFirst());
                  addCurrentId = !typeIndexFilter.shouldBeIgnored(nodeType);
                }
                if (!addCurrentId) {
                  addCurrentId =
                      !aspectIndexFilter.shouldBeIgnored(getNodeAspects(childNodePair.getFirst()));
                }
                if (addCurrentId) {
                  childIds.add(childNodePair.getFirst());
                }
                return true;
              }

              @Override
              public void done() {}
            });
        nodeMetaData.setChildIds(childIds);
      }

      if (includeParentAssociations && !ignoreLargeMetadata) {
        final List<ChildAssociationRef> parentAssocs = new ArrayList<ChildAssociationRef>(100);
        nodeDAO.getParentAssocs(
            nodeId,
            null,
            null,
            null,
            new ChildAssocRefQueryCallback() {
              @Override
              public boolean preLoadNodes() {
                return false;
              }

              @Override
              public boolean orderResults() {
                return false;
              }

              @Override
              public boolean handle(
                  Pair<Long, ChildAssociationRef> childAssocPair,
                  Pair<Long, NodeRef> parentNodePair,
                  Pair<Long, NodeRef> childNodePair) {
                parentAssocs.add(tenantService.getBaseName(childAssocPair.getSecond(), true));
                return true;
              }

              @Override
              public void done() {}
            });
        for (ChildAssociationRef ref : categoryPaths.getCategoryParents()) {
          parentAssocs.add(tenantService.getBaseName(ref, true));
        }

        CRC32 crc = new CRC32();
        for (ChildAssociationRef car : parentAssocs) {
          try {
            crc.update(car.toString().getBytes("UTF-8"));
          } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("UTF-8 encoding is not supported");
          }
        }
        nodeMetaData.setParentAssocs(parentAssocs, crc.getValue());

        // TODO non-child associations
        //                Collection<Pair<Long, AssociationRef>> sourceAssocs =
        // nodeDAO.getSourceNodeAssocs(nodeId);
        //                Collection<Pair<Long, AssociationRef>> targetAssocs =
        // nodeDAO.getTargetNodeAssocs(nodeId);
        //
        //                nodeMetaData.setAssocs();
      }

      if (includeOwner) {
        // cached in OwnableService
        nodeMetaData.setOwner(ownableService.getOwner(status.getNodeRef()));
      }

      rowHandler.processResult(nodeMetaData);
    }
  }
  private CategoryPaths getCategoryPaths(
      NodeRef nodeRef, Set<QName> aspects, Map<QName, Serializable> properties) {
    ArrayList<Pair<Path, QName>> categoryPaths = new ArrayList<Pair<Path, QName>>();
    ArrayList<ChildAssociationRef> categoryParents = new ArrayList<ChildAssociationRef>();

    nodeDAO.setCheckNodeConsistency();
    for (QName classRef : aspects) {
      AspectDefinition aspDef = dictionaryService.getAspect(classRef);
      if (!isCategorised(aspDef)) {
        continue;
      }
      LinkedList<Pair<Path, QName>> aspectPaths = new LinkedList<Pair<Path, QName>>();
      for (PropertyDefinition propDef : aspDef.getProperties().values()) {
        if (!propDef.getDataType().getName().equals(DataTypeDefinition.CATEGORY)) {
          // The property is not a category
          continue;
        }
        // Don't try to iterate if the property is null
        Serializable propVal = properties.get(propDef.getName());
        if (propVal == null) {
          continue;
        }
        for (NodeRef catRef : DefaultTypeConverter.INSTANCE.getCollection(NodeRef.class, propVal)) {
          if (catRef == null) {
            continue;
          }
          // can be running in context of System user, hence use input nodeRef
          catRef = tenantService.getName(nodeRef, catRef);

          try {
            Pair<Long, NodeRef> pair = nodeDAO.getNodePair(catRef);
            if (pair != null) {
              for (Path path : nodeDAO.getPaths(pair, false)) {
                aspectPaths.add(new Pair<Path, QName>(path, aspDef.getName()));
              }
            }
          } catch (InvalidNodeRefException e) {
            // If the category does not exists we move on the next
          }
        }
      }
      categoryPaths.addAll(aspectPaths);
    }
    // Add member final element
    for (Pair<Path, QName> pair : categoryPaths) {
      if (pair.getFirst().last() instanceof Path.ChildAssocElement) {
        Path.ChildAssocElement cae = (Path.ChildAssocElement) pair.getFirst().last();
        ChildAssociationRef assocRef = cae.getRef();
        ChildAssociationRef categoryParentRef =
            new ChildAssociationRef(
                assocRef.getTypeQName(),
                assocRef.getChildRef(),
                QName.createQName("member"),
                nodeRef);
        pair.getFirst().append(new Path.ChildAssocElement(categoryParentRef));
        categoryParents.add(categoryParentRef);
      }
    }

    return new CategoryPaths(categoryPaths, categoryParents);
  }