+\r
+\r
+\r
+ /* (non-Javadoc)\r
+ * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNamesByFullText(java.util.EnumSet, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.Set, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.Map)\r
+ */\r
+ @Override\r
+ public Pager<SearchResult<TaxonBase>> findTaxaAndNamesByFullText(\r
+ EnumSet<TaxaAndNamesSearchMode> searchModes, String queryString, Classification classification,\r
+ Set<NamedArea> namedAreas, Set<PresenceAbsenceTerm> distributionStatus, List<Language> languages,\r
+ boolean highlightFragments, Integer pageSize,\r
+ Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)\r
+ throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {\r
+\r
+ // FIXME: allow taxonomic ordering\r
+ // hql equivalent: order by t.name.genusOrUninomial, case when t.name.specificEpithet like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";\r
+ // this require building a special sort column by a special classBridge\r
+ if(highlightFragments){\r
+ logger.warn("findTaxaAndNamesByFullText() : fragment highlighting is " +\r
+ "currently not fully supported by this method and thus " +\r
+ "may not work with common names and misapplied names.");\r
+ }\r
+\r
+ // convert sets to lists\r
+ List<NamedArea> namedAreaList = null;\r
+ List<PresenceAbsenceTerm>distributionStatusList = null;\r
+ if(namedAreas != null){\r
+ namedAreaList = new ArrayList<NamedArea>(namedAreas.size());\r
+ namedAreaList.addAll(namedAreas);\r
+ }\r
+ if(distributionStatus != null){\r
+ distributionStatusList = new ArrayList<PresenceAbsenceTerm>(distributionStatus.size());\r
+ distributionStatusList.addAll(distributionStatus);\r
+ }\r
+\r
+ // set default if parameter is null\r
+ if(searchModes == null){\r
+ searchModes = EnumSet.of(TaxaAndNamesSearchMode.doTaxa);\r
+ }\r
+\r
+ // set sort order and thus override any sort orders which may have been\r
+ // defindes by prepare*Search methods\r
+ if(orderHints == null){\r
+ orderHints = OrderHint.NOMENCLATURAL_SORT_ORDER;\r
+ }\r
+ SortField[] sortFields = new SortField[orderHints.size()];\r
+ int i = 0;\r
+ for(OrderHint oh : orderHints){\r
+ sortFields[i++] = oh.toSortField();\r
+ }\r
+// SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("id", SortField.STRING, false)};\r
+// SortField[] sortFields = new SortField[]{new SortField(NomenclaturalSortOrderBrigde.NAME_SORT_FIELD_NAME, SortField.STRING, false)};\r
+\r
+\r
+ boolean addDistributionFilter = namedAreas != null && namedAreas.size() > 0;\r
+\r
+ List<LuceneSearch> luceneSearches = new ArrayList<LuceneSearch>();\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+\r
+ /*\r
+ ======== filtering by distribution , HOWTO ========\r
+\r
+ - http://www.javaranch.com/journal/2009/02/filtering-a-lucene-search.html\r
+ - http://stackoverflow.com/questions/17709256/lucene-solr-using-complex-filters -> QueryWrapperFilter\r
+ add Filter to search as http://lucene.apache.org/core/3_6_0/api/all/org/apache/lucene/search/Filter.html\r
+ which will be put into a FilteredQuersy in the end ?\r
+\r
+\r
+ 3. how does it work in spatial?\r
+ see\r
+ - http://www.nsshutdown.com/projects/lucene/whitepaper/locallucene_v2.html\r
+ - http://www.infoq.com/articles/LuceneSpatialSupport\r
+ - http://www.mhaller.de/archives/156-Spatial-search-with-Lucene.html\r
+ ------------------------------------------------------------------------\r
+\r
+ filter strategies:\r
+ A) use a separate distribution filter per index sub-query/search:\r
+ - byTaxonSyonym (query TaxaonBase):\r
+ use a join area filter (Distribution -> TaxonBase)\r
+ - byCommonName (query DescriptionElementBase): use an area filter on\r
+ DescriptionElementBase !!! PROBLEM !!!\r
+ This cannot work since the distributions are different entities than the\r
+ common names and thus these are different lucene documents.\r
+ - byMisaplliedNames (join query TaxonRelationship -> TaxaonBase):\r
+ use a join area filter (Distribution -> TaxonBase)\r
+\r
+ B) use a common distribution filter for all index sub-query/searches:\r
+ - use a common join area filter (Distribution -> TaxonBase)\r
+ - also implement the byCommonName as join query (CommonName -> TaxonBase)\r
+ PROBLEM in this case: we are losing the fragment highlighting for the\r
+ common names, since the returned documents are always TaxonBases\r
+ */\r
+\r
+ /* The QueryFactory for creating filter queries on Distributions should\r
+ * The query factory used for the common names query cannot be reused\r
+ * for this case, since we want to only record the text fields which are\r
+ * actually used in the primary query\r
+ */\r
+ QueryFactory distributionFilterQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Distribution.class);\r
+\r
+ BooleanFilter multiIndexByAreaFilter = new BooleanFilter();\r
+\r
+\r
+ // search for taxa or synonyms\r
+ if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) || searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {\r
+ Class taxonBaseSubclass = TaxonBase.class;\r
+ if(searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && !searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){\r
+ taxonBaseSubclass = Taxon.class;\r
+ } else if (!searchModes.contains(TaxaAndNamesSearchMode.doTaxa) && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)) {\r
+ taxonBaseSubclass = Synonym.class;\r
+ }\r
+ luceneSearches.add(prepareFindByFullTextSearch(taxonBaseSubclass, queryString, classification, languages, highlightFragments, sortFields));\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+ /* A) does not work!!!!\r
+ if(addDistributionFilter){\r
+ // in this case we need a filter which uses a join query\r
+ // to get the TaxonBase documents for the DescriptionElementBase documents\r
+ // which are matching the areas in question\r
+ Query taxonAreaJoinQuery = createByDistributionJoinQuery(\r
+ namedAreaList,\r
+ distributionStatusList,\r
+ distributionFilterQueryFactory\r
+ );\r
+ multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);\r
+ }\r
+ */\r
+ if(addDistributionFilter && searchModes.contains(TaxaAndNamesSearchMode.doSynonyms)){\r
+ // add additional area filter for synonyms\r
+ String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index\r
+ String toField = "accTaxon.id"; // id in TaxonBase index\r
+\r
+ BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);\r
+\r
+ Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);\r
+ multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);\r
+\r
+ }\r
+ }\r
+\r
+ // search by CommonTaxonName\r
+ if(searchModes.contains(TaxaAndNamesSearchMode.doTaxaByCommonNames)) {\r
+ // B)\r
+ QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);\r
+ Query byCommonNameJoinQuery = descriptionElementQueryFactory.newJoinQuery(\r
+ "inDescription.taxon.id",\r
+ "id",\r
+ QueryFactory.addTypeRestriction(\r
+ createByDescriptionElementFullTextQuery(queryString, classification, null, languages, descriptionElementQueryFactory)\r
+ , CommonTaxonName.class\r
+ ),\r
+ CommonTaxonName.class);\r
+ logger.debug("byCommonNameJoinQuery: " + byCommonNameJoinQuery.toString());\r
+ LuceneSearch byCommonNameSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);\r
+ byCommonNameSearch.setCdmTypRestriction(Taxon.class);\r
+ byCommonNameSearch.setQuery(byCommonNameJoinQuery);\r
+ byCommonNameSearch.setSortFields(sortFields);\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+\r
+ luceneSearches.add(byCommonNameSearch);\r
+\r
+ /* A) does not work!!!!\r
+ luceneSearches.add(\r
+ prepareByDescriptionElementFullTextSearch(CommonTaxonName.class,\r
+ queryString, classification, null, languages, highlightFragments)\r
+ );\r
+ idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
+ if(addDistributionFilter){\r
+ // in this case we are able to use DescriptionElementBase documents\r
+ // which are matching the areas in question directly\r
+ BooleanQuery byDistributionQuery = createByDistributionQuery(\r
+ namedAreaList,\r
+ distributionStatusList,\r
+ distributionFilterQueryFactory\r
+ );\r
+ multiIndexByAreaFilter.add(new QueryWrapperFilter(byDistributionQuery), Occur.SHOULD);\r
+ } */\r
+ }\r
+\r
+ // search by misapplied names\r
+ if(searchModes.contains(TaxaAndNamesSearchMode.doMisappliedNames)) {\r
+ // NOTE:\r
+ // prepareFindByTaxonRelationFullTextSearch() is making use of JoinUtil.createJoinQuery()\r
+ // which allows doing query time joins\r
+ // finds the misapplied name (Taxon B) which is an misapplication for\r
+ // a related Taxon A.\r
+ //\r
+ luceneSearches.add(prepareFindByTaxonRelationFullTextSearch(\r
+ new TaxonRelationshipEdge(TaxonRelationshipType.MISAPPLIED_NAME_FOR(), Direction.relatedTo),\r
+ queryString, classification, languages, highlightFragments, sortFields));\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+\r
+ if(addDistributionFilter){\r
+ String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index\r
+\r
+ /*\r
+ * Here i was facing wired and nasty bug which took me bugging be really for hours until I found this solution.\r
+ * Maybe this is a but in java itself java.\r
+ *\r
+ * When the string toField is constructed by using the expression TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString()\r
+ * directly:\r
+ *\r
+ * String toField = "relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id";\r
+ *\r
+ * The byDistributionQuery fails, however when the uuid is first stored in another string variable the query\r
+ * will execute as expected:\r
+ *\r
+ * String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();\r
+ * String toField = "relation." + misappliedNameForUuid +".to.id";\r
+ *\r
+ * Comparing both strings by the String.equals method returns true, so both String are identical.\r
+ *\r
+ * The bug occurs when running eu.etaxonomy.cdm.api.service.TaxonServiceSearchTest in eclipse and in maven and seems to to be\r
+ * dependent from a specific jvm (openjdk6 6b27-1.12.6-1ubuntu0.13.04.2, openjdk7 7u25-2.3.10-1ubuntu0.13.04.2, oracle jdk1.7.0_25 tested)\r
+ * The bug is persistent after a reboot of the development computer.\r
+ */\r
+// String misappliedNameForUuid = TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString();\r
+// String toField = "relation." + misappliedNameForUuid +".to.id";\r
+ String toField = "relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id";\r
+// System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + misappliedNameForUuid +".to.id") ? " > identical" : " > different");\r
+// System.out.println("relation.1ed87175-59dd-437e-959e-0d71583d8417.to.id".equals("relation." + TaxonRelationshipType.MISAPPLIED_NAME_FOR().getUuid().toString() +".to.id") ? " > identical" : " > different");\r
+\r
+ BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, distributionFilterQueryFactory);\r
+ Query taxonAreaJoinQuery = distributionFilterQueryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);\r
+ QueryWrapperFilter filter = new QueryWrapperFilter(taxonAreaJoinQuery);\r
+\r
+// debug code for bug described above\r
+ DocIdSet filterMatchSet = filter.getDocIdSet(luceneIndexToolProvider.getIndexReaderFor(Taxon.class));\r
+// System.err.println(DocIdBitSetPrinter.docsAsString(filterMatchSet, 100));\r
+\r
+ multiIndexByAreaFilter.add(filter, Occur.SHOULD);\r
+ }\r
+ }\r
+\r
+ LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider,\r
+ luceneSearches.toArray(new LuceneSearch[luceneSearches.size()]));\r
+\r
+\r
+ if(addDistributionFilter){\r
+\r
+ // B)\r
+ // in this case we need a filter which uses a join query\r
+ // to get the TaxonBase documents for the DescriptionElementBase documents\r
+ // which are matching the areas in question\r
+ //\r
+ // for toTaxa, doByCommonName\r
+ Query taxonAreaJoinQuery = createByDistributionJoinQuery(\r
+ namedAreaList,\r
+ distributionStatusList,\r
+ distributionFilterQueryFactory\r
+ );\r
+ multiIndexByAreaFilter.add(new QueryWrapperFilter(taxonAreaJoinQuery), Occur.SHOULD);\r
+ }\r
+\r
+ if (addDistributionFilter){\r
+ multiSearch.setFilter(multiIndexByAreaFilter);\r
+ }\r
+\r
+\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);\r
+\r
+ // --- initialize taxa, highlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());\r
+\r
+\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+ }\r
+\r
+ /**\r
+ * @param namedAreaList at least one area must be in the list\r
+ * @param distributionStatusList optional\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ protected Query createByDistributionJoinQuery(\r
+ List<NamedArea> namedAreaList,\r
+ List<PresenceAbsenceTerm> distributionStatusList,\r
+ QueryFactory queryFactory\r
+ ) throws IOException {\r
+\r
+ String fromField = "inDescription.taxon.id"; // in DescriptionElementBase index\r
+ String toField = "id"; // id in TaxonBase index\r
+\r
+ BooleanQuery byDistributionQuery = createByDistributionQuery(namedAreaList, distributionStatusList, queryFactory);\r
+\r
+ Query taxonAreaJoinQuery = queryFactory.newJoinQuery(fromField, toField, byDistributionQuery, Distribution.class);\r
+\r
+ return taxonAreaJoinQuery;\r
+ }\r
+\r
+ /**\r
+ * @param namedAreaList\r
+ * @param distributionStatusList\r
+ * @param queryFactory\r
+ * @return\r
+ */\r
+ private BooleanQuery createByDistributionQuery(List<NamedArea> namedAreaList,\r
+ List<PresenceAbsenceTerm> distributionStatusList, QueryFactory queryFactory) {\r
+ BooleanQuery areaQuery = new BooleanQuery();\r
+ // area field from Distribution\r
+ areaQuery.add(queryFactory.newEntityIdsQuery("area.id", namedAreaList), Occur.MUST);\r
+\r
+ // status field from Distribution\r
+ if(distributionStatusList != null && distributionStatusList.size() > 0){\r
+ areaQuery.add(queryFactory.newEntityIdsQuery("status.id", distributionStatusList), Occur.MUST);\r
+ }\r
+\r
+ logger.debug("createByDistributionQuery() query: " + areaQuery.toString());\r
+ return areaQuery;\r
+ }\r
+\r
+ /**\r
+ * This method has been primarily created for testing the area join query but might\r
+ * also be useful in other situations\r
+ *\r
+ * @param namedAreaList\r
+ * @param distributionStatusList\r
+ * @param classification\r
+ * @param highlightFragments\r
+ * @return\r
+ * @throws IOException\r
+ */\r
+ protected LuceneSearch prepareByDistributionSearch(\r
+ List<NamedArea> namedAreaList, List<PresenceAbsenceTerm> distributionStatusList,\r
+ Classification classification) throws IOException {\r
+\r
+ BooleanQuery finalQuery = new BooleanQuery();\r
+\r
+ LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, Taxon.class);\r
+\r
+ // FIXME is this query factory using the wrong type?\r
+ QueryFactory taxonQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(Taxon.class);\r
+\r
+ SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING, false)};\r
+ luceneSearch.setSortFields(sortFields);\r
+\r
+\r
+ Query byAreaQuery = createByDistributionJoinQuery(namedAreaList, distributionStatusList, taxonQueryFactory);\r
+\r
+ finalQuery.add(byAreaQuery, Occur.MUST);\r
+\r
+ if(classification != null){\r
+ finalQuery.add(taxonQueryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);\r
+ }\r
+\r
+ logger.info("prepareByAreaSearch() query: " + finalQuery.toString());\r
+ luceneSearch.setQuery(finalQuery);\r
+\r
+ return luceneSearch;\r
+ }\r
+\r
+\r
+\r
+ /* (non-Javadoc)\r
+ * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByDescriptionElementFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)\r
+ */\r
+ @Override\r
+ public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(\r
+ Class<? extends DescriptionElementBase> clazz, String queryString,\r
+ Classification classification, List<Feature> features, List<Language> languages,\r
+ boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {\r
+\r
+\r
+ LuceneSearch luceneSearch = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, features, languages, highlightFragments);\r
+\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);\r
+\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+ idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
+\r
+ // --- initialize taxa, highlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());\r
+ @SuppressWarnings("rawtypes")\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+\r
+ }\r
+\r
+\r
+ @Override\r
+ public Pager<SearchResult<TaxonBase>> findByEverythingFullText(String queryString,\r
+ Classification classification, List<Language> languages, boolean highlightFragments,\r
+ Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException, LuceneMultiSearchException {\r
+\r
+ LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments);\r
+ LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments, null);\r
+\r
+ LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneIndexToolProvider, luceneSearchByDescriptionElement, luceneSearchByTaxonBase);\r
+\r
+ // --- execute search\r
+ TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);\r
+\r
+ // --- initialize taxa, highlight matches ....\r
+ ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());\r
+\r
+ Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();\r
+ idFieldMap.put(CdmBaseType.TAXON, "id");\r
+ idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");\r
+\r
+ List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(\r
+ topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);\r
+\r
+ int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;\r
+ return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * @param clazz\r
+ * @param queryString\r
+ * @param classification\r
+ * @param features\r
+ * @param languages\r
+ * @param highlightFragments\r
+ * @param directorySelectClass\r
+ * @return\r
+ */\r
+ protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz,\r
+ String queryString, Classification classification, List<Feature> features,\r
+ List<Language> languages, boolean highlightFragments) {\r
+\r
+ LuceneSearch luceneSearch = new LuceneSearch(luceneIndexToolProvider, GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, DescriptionElementBase.class);\r
+ QueryFactory descriptionElementQueryFactory = luceneIndexToolProvider.newQueryFactoryFor(DescriptionElementBase.class);\r
+\r
+ SortField[] sortFields = new SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", SortField.STRING, false)};\r
+\r
+ BooleanQuery finalQuery = createByDescriptionElementFullTextQuery(queryString, classification, features,\r
+ languages, descriptionElementQueryFactory);\r
+\r
+ luceneSearch.setSortFields(sortFields);\r
+ luceneSearch.setCdmTypRestriction(clazz);\r
+ luceneSearch.setQuery(finalQuery);\r
+ if(highlightFragments){\r
+ luceneSearch.setHighlightFields(descriptionElementQueryFactory.getTextFieldNamesAsArray());\r
+ }\r
+\r
+ return luceneSearch;\r
+ }\r
+\r
+ /**\r
+ * @param queryString\r
+ * @param classification\r
+ * @param features\r
+ * @param languages\r
+ * @param descriptionElementQueryFactory\r
+ * @return\r
+ */\r
+ private BooleanQuery createByDescriptionElementFullTextQuery(String queryString, Classification classification,\r
+ List<Feature> features, List<Language> languages, QueryFactory descriptionElementQueryFactory) {\r
+ BooleanQuery finalQuery = new BooleanQuery();\r
+ BooleanQuery textQuery = new BooleanQuery();\r
+ textQuery.add(descriptionElementQueryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);\r
+\r
+ // common name\r
+ Query nameQuery;\r
+ if(languages == null || languages.size() == 0){\r
+ nameQuery = descriptionElementQueryFactory.newTermQuery("name", queryString);\r
+ } else {\r
+ nameQuery = new BooleanQuery();\r
+ BooleanQuery languageSubQuery = new BooleanQuery();\r
+ for(Language lang : languages){\r
+ languageSubQuery.add(descriptionElementQueryFactory.newTermQuery("language.uuid", lang.getUuid().toString(), false), Occur.SHOULD);\r
+ }\r
+ ((BooleanQuery) nameQuery).add(descriptionElementQueryFactory.newTermQuery("name", queryString), Occur.MUST);\r
+ ((BooleanQuery) nameQuery).add(languageSubQuery, Occur.MUST);\r
+ }\r
+ textQuery.add(nameQuery, Occur.SHOULD);\r
+\r
+\r
+ // text field from TextData\r
+ textQuery.add(descriptionElementQueryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);\r
+\r
+ // --- TermBase fields - by representation ----\r
+ // state field from CategoricalData\r
+ textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.state", queryString, languages), Occur.SHOULD);\r
+\r
+ // state field from CategoricalData\r
+ textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("stateData.modifyingText", queryString, languages), Occur.SHOULD);\r
+\r
+ // area field from Distribution\r
+ textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);\r
+\r
+ // status field from Distribution\r
+ textQuery.add(descriptionElementQueryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD);\r
+\r
+ finalQuery.add(textQuery, Occur.MUST);\r
+ // --- classification ----\r
+\r
+ if(classification != null){\r
+ finalQuery.add(descriptionElementQueryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);\r
+ }\r
+\r
+ // --- IdentifieableEntity fields - by uuid\r
+ if(features != null && features.size() > 0 ){\r
+ finalQuery.add(descriptionElementQueryFactory.newEntityUuidsQuery("feature.uuid", features), Occur.MUST);\r
+ }\r
+\r
+ // the description must be associated with a taxon\r
+ finalQuery.add(descriptionElementQueryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);\r
+\r
+ logger.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery.toString());\r
+ return finalQuery;\r
+ }\r
+\r
+ /**\r
+ * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}\r
+ * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.\r
+ * This method is a convenient means to retrieve a Lucene query string for such the fields.\r
+ *\r
+ * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}\r
+ * or {@link MultilanguageTextFieldBridge }\r
+ * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages\r
+ * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned\r
+ * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.\r
+ *\r
+ * TODO move to utiliy class !!!!!!!!\r
+ */\r
+ private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {\r
+\r
+ if(stringBuilder == null){\r
+ stringBuilder = new StringBuilder();\r
+ }\r
+ if(languages == null || languages.size() == 0){\r
+ stringBuilder.append(name + ".ALL:(%1$s) ");\r
+ } else {\r
+ for(Language lang : languages){\r
+ stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");\r
+ }\r
+ }\r
+ return stringBuilder;\r
+ }\r
+\r
+ @Override\r
+ public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){\r
+ List <Synonym> inferredSynonyms = new ArrayList<Synonym>();\r
+ List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();\r
+\r
+ HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();\r
+\r
+\r
+ UUID nameUuid= taxon.getName().getUuid();\r
+ ZoologicalName taxonName = getZoologicalName(nameUuid, zooHashMap);\r
+ String epithetOfTaxon = null;\r
+ String infragenericEpithetOfTaxon = null;\r
+ String infraspecificEpithetOfTaxon = null;\r
+ if (taxonName.isSpecies()){\r
+ epithetOfTaxon= taxonName.getSpecificEpithet();\r
+ } else if (taxonName.isInfraGeneric()){\r
+ infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();\r
+ } else if (taxonName.isInfraSpecific()){\r
+ infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();\r
+ }\r
+ String genusOfTaxon = taxonName.getGenusOrUninomial();\r
+ Set<TaxonNode> nodes = taxon.getTaxonNodes();\r
+ List<String> taxonNames = new ArrayList<String>();\r
+\r
+ for (TaxonNode node: nodes){\r
+ // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName\r
+ // List<String> synonymsEpithet = new ArrayList<String>();\r
+\r
+ if (node.getClassification().equals(classification)){\r
+ if (!node.isTopmostNode()){\r
+ TaxonNode parent = node.getParent();\r
+ parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);\r
+ TaxonNameBase<?,?> parentName = parent.getTaxon().getName();\r
+ ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);\r
+ Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());\r
+ Rank rankOfTaxon = taxonName.getRank();\r
+\r
+\r
+ //create inferred synonyms for species, subspecies\r
+ if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){\r
+\r
+ Synonym inferredEpithet = null;\r
+ Synonym inferredGenus = null;\r
+ Synonym potentialCombination = null;\r
+\r
+ List<String> propertyPaths = new ArrayList<String>();\r
+ propertyPaths.add("synonym");\r
+ propertyPaths.add("synonym.name");\r
+ List<OrderHint> orderHints = new ArrayList<OrderHint>();\r
+ orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));\r
+\r
+ List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);\r
+ List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);\r
+\r
+ List<TaxonRelationship> taxonRelListParent = null;\r
+ List<TaxonRelationship> taxonRelListTaxon = null;\r
+ if (doWithMisappliedNames){\r
+ taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);\r
+ taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);\r
+ }\r
+\r
+\r
+ if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){\r
+\r
+\r
+ for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){\r
+ Synonym syn = synonymRelationOfParent.getSynonym();\r
+\r
+ inferredEpithet = createInferredEpithets(taxon,\r
+ zooHashMap, taxonName, epithetOfTaxon,\r
+ infragenericEpithetOfTaxon,\r
+ infraspecificEpithetOfTaxon,\r
+ taxonNames, parentName,\r
+ syn);\r
+\r
+\r
+ inferredSynonyms.add(inferredEpithet);\r
+ zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());\r
+ taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());\r
+ }\r
+\r
+ if (doWithMisappliedNames){\r
+\r
+ for (TaxonRelationship taxonRelationship: taxonRelListParent){\r
+ Taxon misappliedName = taxonRelationship.getFromTaxon();\r
+\r
+ inferredEpithet = createInferredEpithets(taxon,\r
+ zooHashMap, taxonName, epithetOfTaxon,\r
+ infragenericEpithetOfTaxon,\r
+ infraspecificEpithetOfTaxon,\r
+ taxonNames, parentName,\r
+ misappliedName);\r
+\r
+ inferredSynonyms.add(inferredEpithet);\r
+ zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());\r
+ taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());\r
+ }\r
+ }\r
+\r
+ if (!taxonNames.isEmpty()){\r
+ List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);\r
+ ZoologicalName name;\r
+ if (!synNotInCDM.isEmpty()){\r
+ inferredSynonymsToBeRemoved.clear();\r
+\r
+ for (Synonym syn :inferredSynonyms){\r
+ name = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
+ if (!synNotInCDM.contains(name.getNameCache())){\r
+ inferredSynonymsToBeRemoved.add(syn);\r
+ }\r
+ }\r
+\r
+ // Remove identified Synonyms from inferredSynonyms\r
+ for (Synonym synonym : inferredSynonymsToBeRemoved) {\r
+ inferredSynonyms.remove(synonym);\r
+ }\r
+ }\r
+ }\r
+\r
+ }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){\r
+\r
+\r
+ for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){\r
+ TaxonNameBase synName;\r
+ ZoologicalName inferredSynName;\r
+\r
+ Synonym syn = synonymRelationOfTaxon.getSynonym();\r
+ inferredGenus = createInferredGenus(taxon,\r
+ zooHashMap, taxonName, epithetOfTaxon,\r
+ genusOfTaxon, taxonNames, zooParentName, syn);\r
+\r
+ inferredSynonyms.add(inferredGenus);\r
+ zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());\r
+ taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());\r
+\r
+\r
+ }\r
+\r
+ if (doWithMisappliedNames){\r
+\r
+ for (TaxonRelationship taxonRelationship: taxonRelListTaxon){\r
+ Taxon misappliedName = taxonRelationship.getFromTaxon();\r
+ inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName, misappliedName);\r
+\r
+ inferredSynonyms.add(inferredGenus);\r
+ zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());\r
+ taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());\r
+ }\r
+ }\r
+\r
+\r
+ if (!taxonNames.isEmpty()){\r
+ List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);\r
+ ZoologicalName name;\r
+ if (!synNotInCDM.isEmpty()){\r
+ inferredSynonymsToBeRemoved.clear();\r
+\r
+ for (Synonym syn :inferredSynonyms){\r
+ name = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
+ if (!synNotInCDM.contains(name.getNameCache())){\r
+ inferredSynonymsToBeRemoved.add(syn);\r
+ }\r
+ }\r
+\r
+ // Remove identified Synonyms from inferredSynonyms\r
+ for (Synonym synonym : inferredSynonymsToBeRemoved) {\r
+ inferredSynonyms.remove(synonym);\r
+ }\r
+ }\r
+ }\r
+\r
+ }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){\r
+\r
+ Reference sourceReference = null; // TODO: Determination of sourceReference is redundant\r
+ ZoologicalName inferredSynName;\r
+ //for all synonyms of the parent...\r
+ for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){\r
+ TaxonNameBase synName;\r
+ Synonym synParent = synonymRelationOfParent.getSynonym();\r
+ synName = synParent.getName();\r
+\r
+ HibernateProxyHelper.deproxy(synParent);\r
+\r
+ // Set the sourceReference\r
+ sourceReference = synParent.getSec();\r
+\r
+ // Determine the idInSource\r
+ String idInSourceParent = getIdInSource(synParent);\r
+\r
+ ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);\r
+ String synParentGenus = parentSynZooName.getGenusOrUninomial();\r
+ String synParentInfragenericName = null;\r
+ String synParentSpecificEpithet = null;\r
+\r
+ if (parentSynZooName.isInfraGeneric()){\r
+ synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();\r
+ }\r
+ if (parentSynZooName.isSpecies()){\r
+ synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();\r
+ }\r
+\r
+ /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){\r
+ synonymsGenus.put(synGenusName, idInSource);\r
+ }*/\r
+\r
+ //for all synonyms of the taxon\r
+\r
+ for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){\r
+\r
+ Synonym syn = synonymRelationOfTaxon.getSynonym();\r
+ ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
+ potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,\r
+ synParentGenus,\r
+ synParentInfragenericName,\r
+ synParentSpecificEpithet, syn, zooHashMap);\r
+\r
+ taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());\r
+ inferredSynonyms.add(potentialCombination);\r
+ zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());\r
+ taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());\r
+\r
+ }\r
+\r
+\r
+ }\r
+\r
+ if (doWithMisappliedNames){\r
+\r
+ for (TaxonRelationship parentRelationship: taxonRelListParent){\r
+\r
+ TaxonNameBase misappliedParentName;\r
+\r
+ Taxon misappliedParent = parentRelationship.getFromTaxon();\r
+ misappliedParentName = misappliedParent.getName();\r
+\r
+ HibernateProxyHelper.deproxy(misappliedParent);\r
+\r
+ // Set the sourceReference\r
+ sourceReference = misappliedParent.getSec();\r
+\r
+ // Determine the idInSource\r
+ String idInSourceParent = getIdInSource(misappliedParent);\r
+\r
+ ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);\r
+ String synParentGenus = parentSynZooName.getGenusOrUninomial();\r
+ String synParentInfragenericName = null;\r
+ String synParentSpecificEpithet = null;\r
+\r
+ if (parentSynZooName.isInfraGeneric()){\r
+ synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();\r
+ }\r
+ if (parentSynZooName.isSpecies()){\r
+ synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();\r
+ }\r
+\r
+\r
+ for (TaxonRelationship taxonRelationship: taxonRelListTaxon){\r
+ Taxon misappliedName = taxonRelationship.getFromTaxon();\r
+ ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);\r
+ potentialCombination = createPotentialCombination(\r
+ idInSourceParent, parentSynZooName, zooMisappliedName,\r
+ synParentGenus,\r
+ synParentInfragenericName,\r
+ synParentSpecificEpithet, misappliedName, zooHashMap);\r
+\r
+\r
+ taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());\r
+ inferredSynonyms.add(potentialCombination);\r
+ zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());\r
+ taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());\r
+ }\r
+ }\r
+ }\r
+\r
+ if (!taxonNames.isEmpty()){\r
+ List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);\r
+ ZoologicalName name;\r
+ if (!synNotInCDM.isEmpty()){\r
+ inferredSynonymsToBeRemoved.clear();\r
+ for (Synonym syn :inferredSynonyms){\r
+ try{\r
+ name = (ZoologicalName) syn.getName();\r
+ }catch (ClassCastException e){\r
+ name = getZoologicalName(syn.getName().getUuid(), zooHashMap);\r
+ }\r
+ if (!synNotInCDM.contains(name.getNameCache())){\r
+ inferredSynonymsToBeRemoved.add(syn);\r
+ }\r
+ }\r
+ // Remove identified Synonyms from inferredSynonyms\r
+ for (Synonym synonym : inferredSynonymsToBeRemoved) {\r
+ inferredSynonyms.remove(synonym);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }else {\r
+ logger.info("The synonymrelationship type is not defined.");\r
+ return inferredSynonyms;\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ return inferredSynonyms;\r
+ }\r
+\r
+ private Synonym createPotentialCombination(String idInSourceParent,\r
+ ZoologicalName parentSynZooName, ZoologicalName zooSynName, String synParentGenus,\r
+ String synParentInfragenericName, String synParentSpecificEpithet,\r
+ TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {\r
+ Synonym potentialCombination;\r
+ Reference sourceReference;\r
+ ZoologicalName inferredSynName;\r
+ HibernateProxyHelper.deproxy(syn);\r
+\r
+ // Set sourceReference\r
+ sourceReference = syn.getSec();\r
+ if (sourceReference == null){\r
+ logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");\r
+ //TODO:Remove\r
+ if (!parentSynZooName.getTaxa().isEmpty()){\r
+ TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();\r
+\r
+ sourceReference = taxon.getSec();\r
+ }\r
+ }\r
+ String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();\r
+\r
+ String synTaxonInfraSpecificName= null;\r
+\r
+ if (parentSynZooName.isSpecies()){\r
+ synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();\r
+ }\r
+\r
+ /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){\r
+ synonymsEpithet.add(epithetName);\r
+ }*/\r
+\r
+ //create potential combinations...\r
+ inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());\r
+\r
+ inferredSynName.setGenusOrUninomial(synParentGenus);\r
+ if (zooSynName.isSpecies()){\r
+ inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);\r
+ if (parentSynZooName.isInfraGeneric()){\r
+ inferredSynName.setInfraGenericEpithet(synParentInfragenericName);\r
+ }\r
+ }\r
+ if (zooSynName.isInfraSpecific()){\r
+ inferredSynName.setSpecificEpithet(synParentSpecificEpithet);\r
+ inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);\r
+ }\r
+ if (parentSynZooName.isInfraGeneric()){\r
+ inferredSynName.setInfraGenericEpithet(synParentInfragenericName);\r
+ }\r
+\r
+\r
+ potentialCombination = Synonym.NewInstance(inferredSynName, null);\r
+\r
+ // Set the sourceReference\r
+ potentialCombination.setSec(sourceReference);\r
+\r
+\r
+ // Determine the idInSource\r
+ String idInSourceSyn= getIdInSource(syn);\r
+\r
+ if (idInSourceParent != null && idInSourceSyn != null) {\r
+ IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
+ inferredSynName.addSource(originalSource);\r
+ originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation, idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);\r
+ potentialCombination.addSource(originalSource);\r
+ }\r
+\r
+ return potentialCombination;\r
+ }\r
+\r
+ private Synonym createInferredGenus(Taxon taxon,\r
+ HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,\r
+ String epithetOfTaxon, String genusOfTaxon,\r
+ List<String> taxonNames, ZoologicalName zooParentName,\r
+ TaxonBase syn) {\r
+\r
+ Synonym inferredGenus;\r
+ TaxonNameBase synName;\r
+ ZoologicalName inferredSynName;\r
+ synName =syn.getName();\r
+ HibernateProxyHelper.deproxy(syn);\r
+\r
+ // Determine the idInSource\r
+ String idInSourceSyn = getIdInSource(syn);\r
+ String idInSourceTaxon = getIdInSource(taxon);\r
+ // Determine the sourceReference\r
+ Reference sourceReference = syn.getSec();\r
+\r
+ //logger.warn(sourceReference.getTitleCache());\r
+\r
+ synName = syn.getName();\r
+ ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);\r
+ String synSpeciesEpithetName = synZooName.getSpecificEpithet();\r
+ /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){\r
+ synonymsEpithet.add(synSpeciesEpithetName);\r
+ }*/\r
+\r
+ inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());\r
+ //TODO:differ between parent is genus and taxon is species, parent is subgenus and taxon is species, parent is species and taxon is subspecies and parent is genus and taxon is subgenus...\r
+\r
+\r
+ inferredSynName.setGenusOrUninomial(genusOfTaxon);\r
+ if (zooParentName.isInfraGeneric()){\r
+ inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());\r
+ }\r
+\r
+ if (taxonName.isSpecies()){\r
+ inferredSynName.setSpecificEpithet(synSpeciesEpithetName);\r
+ }\r
+ if (taxonName.isInfraSpecific()){\r
+ inferredSynName.setSpecificEpithet(epithetOfTaxon);\r
+ inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());\r
+ }\r
+\r
+\r
+ inferredGenus = Synonym.NewInstance(inferredSynName, null);\r
+\r
+ // Set the sourceReference\r
+ inferredGenus.setSec(sourceReference);\r
+\r
+ // Add the original source\r
+ if (idInSourceSyn != null && idInSourceTaxon != null) {\r
+ IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+ inferredGenus.addSource(originalSource);\r
+\r
+ originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+ inferredSynName.addSource(originalSource);\r
+ originalSource = null;\r
+\r
+ }else{\r
+ logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");\r
+ IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+ inferredGenus.addSource(originalSource);\r
+\r
+ originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);\r
+ inferredSynName.addSource(originalSource);\r
+ originalSource = null;\r
+ }\r
+\r
+ taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());\r
+\r
+ return inferredGenus;\r
+ }\r
+\r
+ private Synonym createInferredEpithets(Taxon taxon,\r
+ HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,\r
+ String epithetOfTaxon, String infragenericEpithetOfTaxon,\r
+ String infraspecificEpithetOfTaxon, List<String> taxonNames,\r
+ TaxonNameBase parentName, TaxonBase syn) {\r
+\r
+ Synonym inferredEpithet;\r
+ TaxonNameBase<?,?> synName;\r
+ ZoologicalName inferredSynName;\r
+ HibernateProxyHelper.deproxy(syn);\r
+\r
+ // Determine the idInSource\r
+ String idInSourceSyn = getIdInSource(syn);\r
+ String idInSourceTaxon = getIdInSource(taxon);\r
+ // Determine the sourceReference\r
+ Reference<?> sourceReference = syn.getSec();\r
+\r
+ if (sourceReference == null){\r
+ logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());\r
+ sourceReference = taxon.getSec();\r
+ }\r
+\r
+ synName = syn.getName();\r
+ ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);\r
+ String synGenusName = zooSynName.getGenusOrUninomial();\r
+ String synInfraGenericEpithet = null;\r
+ String synSpecificEpithet = null;\r
+\r
+ if (zooSynName.getInfraGenericEpithet() != null){\r
+ synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();\r
+ }\r
+\r
+ if (zooSynName.isInfraSpecific()){\r
+ synSpecificEpithet = zooSynName.getSpecificEpithet();\r
+ }\r
+\r
+ /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){\r
+ synonymsGenus.put(synGenusName, idInSource);\r
+ }*/\r
+\r
+ inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());\r
+\r
+ // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!\r
+ if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {\r
+ logger.error("This specificEpithet is NULL" + taxon.getTitleCache());\r
+ }\r
+ inferredSynName.setGenusOrUninomial(synGenusName);\r
+\r
+ if (parentName.isInfraGeneric()){\r
+ inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);\r
+ }\r
+ if (taxonName.isSpecies()){\r
+ inferredSynName.setSpecificEpithet(epithetOfTaxon);\r
+ }else if (taxonName.isInfraSpecific()){\r
+ inferredSynName.setSpecificEpithet(synSpecificEpithet);\r
+ inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);\r
+ }\r
+\r
+ inferredEpithet = Synonym.NewInstance(inferredSynName, null);\r
+\r
+ // Set the sourceReference\r
+ inferredEpithet.setSec(sourceReference);\r
+\r
+ /* Add the original source\r
+ if (idInSource != null) {\r
+ IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);\r
+\r
+ // Add the citation\r
+ Reference citation = getCitation(syn);\r
+ if (citation != null) {\r
+ originalSource.setCitation(citation);\r
+ inferredEpithet.addSource(originalSource);\r
+ }\r
+ }*/\r
+ String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;\r
+\r
+\r
+ IdentifiableSource originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
+\r
+ inferredEpithet.addSource(originalSource);\r
+\r
+ originalSource = IdentifiableSource.NewInstance(OriginalSourceType.Transformation,\r
+ taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);\r
+\r
+ inferredSynName.addSource(originalSource);\r
+\r
+\r
+\r
+ taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());\r
+\r
+ return inferredEpithet;\r
+ }\r
+\r
+ /**\r
+ * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.\r
+ * Very likely only useful for createInferredSynonyms().\r
+ * @param uuid\r
+ * @param zooHashMap\r
+ * @return\r
+ */\r
+ private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {\r
+ ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);\r
+ if (taxonName == null) {\r
+ taxonName = zooHashMap.get(uuid);\r
+ }\r
+ return taxonName;\r
+ }\r
+\r
+ /**\r
+ * Returns the idInSource for a given Synonym.\r
+ * @param syn\r
+ */\r
+ private String getIdInSource(TaxonBase taxonBase) {\r
+ String idInSource = null;\r
+ Set<IdentifiableSource> sources = taxonBase.getSources();\r
+ if (sources.size() == 1) {\r
+ IdentifiableSource source = sources.iterator().next();\r
+ if (source != null) {\r
+ idInSource = source.getIdInSource();\r
+ }\r
+ } else if (sources.size() > 1) {\r
+ int count = 1;\r
+ idInSource = "";\r
+ for (IdentifiableSource source : sources) {\r
+ idInSource += source.getIdInSource();\r
+ if (count < sources.size()) {\r
+ idInSource += "; ";\r
+ }\r
+ count++;\r
+ }\r
+ } else if (sources.size() == 0){\r
+ logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());\r
+ }\r
+\r
+\r
+ return idInSource;\r
+ }\r
+\r
+\r
+ /**\r
+ * Returns the citation for a given Synonym.\r
+ * @param syn\r
+ */\r
+ private Reference getCitation(Synonym syn) {\r
+ Reference citation = null;\r
+ Set<IdentifiableSource> sources = syn.getSources();\r
+ if (sources.size() == 1) {\r
+ IdentifiableSource source = sources.iterator().next();\r
+ if (source != null) {\r
+ citation = source.getCitation();\r
+ }\r
+ } else if (sources.size() > 1) {\r
+ logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");\r
+ }\r
+\r
+ return citation;\r
+ }\r
+\r
+ @Override\r
+ public List<Synonym> createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){\r
+ List <Synonym> inferredSynonyms = new ArrayList<Synonym>();\r
+\r
+ inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));\r
+ inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));\r
+ inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));\r
+\r
+ return inferredSynonyms;\r
+ }\r
+\r
+ @Override\r
+ public List<Classification> listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List<String> propertyPaths) {\r
+\r
+ // TODO quickly implemented, create according dao !!!!\r
+ Set<TaxonNode> nodes = new HashSet<TaxonNode>();\r
+ Set<Classification> classifications = new HashSet<Classification>();\r
+ List<Classification> list = new ArrayList<Classification>();\r
+\r
+ if (taxonBase == null) {\r
+ return list;\r
+ }\r
+\r
+ taxonBase = load(taxonBase.getUuid());\r
+\r
+ if (taxonBase instanceof Taxon) {\r
+ nodes.addAll(((Taxon)taxonBase).getTaxonNodes());\r
+ } else {\r
+ for (Taxon taxon : ((Synonym)taxonBase).getAcceptedTaxa() ) {\r
+ nodes.addAll(taxon.getTaxonNodes());\r
+ }\r
+ }\r
+ for (TaxonNode node : nodes) {\r
+ classifications.add(node.getClassification());\r
+ }\r
+ list.addAll(classifications);\r
+ return list;\r
+ }\r
+\r
+ @Override\r
+ public Synonym changeRelatedTaxonToSynonym(Taxon fromTaxon, Taxon toTaxon, TaxonRelationshipType oldRelationshipType,\r
+ SynonymRelationshipType synonymRelationshipType) throws DataChangeNoRollbackException {\r
+ // Create new synonym using concept name\r
+ TaxonNameBase<?, ?> synonymName = fromTaxon.getName();\r
+ Synonym synonym = Synonym.NewInstance(synonymName, fromTaxon.getSec());\r
+\r
+ // Remove concept relation from taxon\r
+ toTaxon.removeTaxon(fromTaxon, oldRelationshipType);\r
+\r
+\r
+\r
+\r
+ // Create a new synonym for the taxon\r
+ SynonymRelationship synonymRelationship;\r
+ if (synonymRelationshipType != null\r
+ && synonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF())){\r
+ synonymRelationship = toTaxon.addHomotypicSynonym(synonym, null, null);\r
+ } else{\r
+ synonymRelationship = toTaxon.addHeterotypicSynonymName(synonymName);\r
+ }\r
+\r
+ this.saveOrUpdate(toTaxon);\r
+ //TODO: configurator and classification\r
+ TaxonDeletionConfigurator config = new TaxonDeletionConfigurator();\r
+ config.setDeleteNameIfPossible(false);\r
+ this.deleteTaxon(fromTaxon, config, null);\r
+ return synonymRelationship.getSynonym();\r
+\r
+ }\r
+ @Override\r
+ public DeleteResult isDeletable(TaxonBase taxonBase, DeleteConfiguratorBase config){\r
+ DeleteResult result = new DeleteResult();\r
+ Set<CdmBase> references = commonService.getReferencingObjectsForDeletion(taxonBase);\r
+ if (taxonBase instanceof Taxon){\r
+ TaxonDeletionConfigurator taxonConfig = (TaxonDeletionConfigurator) config;\r
+ result = isDeletableForTaxon(references, taxonConfig);\r
+ }else{\r
+ SynonymDeletionConfigurator synonymConfig = (SynonymDeletionConfigurator) config;\r
+ result = isDeletableForSynonym(references, synonymConfig);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ private DeleteResult isDeletableForSynonym(Set<CdmBase> references, SynonymDeletionConfigurator config){\r
+ String message;\r
+ DeleteResult result = new DeleteResult();\r
+ for (CdmBase ref: references){\r
+ if (!(ref instanceof SynonymRelationship || ref instanceof Taxon || ref instanceof TaxonNameBase )){\r
+ message = "The Synonym can't be deleted as long as it is referenced by " + ref.getClass().getSimpleName() + " with id "+ ref.getId();\r
+ result.addException(new ReferencedObjectUndeletableException(message));\r
+ result.addRelatedObject(ref);\r
+ result.setAbort();\r
+ }\r
+ }\r
+\r
+ return result;\r
+ }\r
+ private DeleteResult isDeletableForTaxon(Set<CdmBase> references, TaxonDeletionConfigurator config){\r
+ String message = null;\r
+ DeleteResult result = new DeleteResult();\r
+ for (CdmBase ref: references){\r
+ if (!(ref instanceof TaxonNameBase)){\r
+ if (!config.isDeleteSynonymRelations() && (ref instanceof SynonymRelationship)){\r
+ message = "The Taxon can't be deleted as long as it has synonyms.";\r
+\r
+ }\r
+ if (!config.isDeleteDescriptions() && (ref instanceof DescriptionBase)){\r
+ message = "The Taxon can't be deleted as long as it has factual data.";\r
+\r
+ }\r
+\r
+ if (!config.isDeleteTaxonNodes() && (ref instanceof TaxonNode)){\r
+ message = "The Taxon can't be deleted as long as it belongs to a taxon node.";\r
+\r
+ }\r
+ if (!config.isDeleteTaxonRelationships() && (ref instanceof TaxonNode)){\r
+ if (!config.isDeleteMisappliedNamesAndInvalidDesignations() && (((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())|| ((TaxonRelationship)ref).getType().equals(TaxonRelationshipType.INVALID_DESIGNATION_FOR()))){\r
+ message = "The Taxon can't be deleted as long as it has misapplied names or invalid designations.";\r
+\r
+ } else{\r
+ message = "The Taxon can't be deleted as long as it belongs to a taxon node.";\r
+\r
+ }\r
+ }\r
+ if (ref instanceof PolytomousKeyNode){\r
+ message = "The Taxon can't be deleted as long as it is referenced by a polytomous key node.";\r
+\r
+ }\r
+\r
+ if (HibernateProxyHelper.isInstanceOf(ref, IIdentificationKey.class)){\r
+ message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";\r
+\r
+\r
+ }\r
+\r
+\r
+ /* //PolytomousKeyNode\r
+ if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){\r
+ String message = "Taxon" + taxon.getTitleCache() + " can't be deleted as it is used in polytomous key node";\r
+ return message;\r
+ }*/\r
+\r
+ //TaxonInteraction\r
+ if (ref.isInstanceOf(TaxonInteraction.class)){\r
+ message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";\r
+\r
+ }\r
+\r
+ //TaxonInteraction\r
+ if (ref.isInstanceOf(DeterminationEvent.class)){\r
+ message = "Taxon can't be deleted as it is used in a determination event";\r
+\r
+ }\r
+\r
+ }\r
+ if (message != null){\r
+ result.addException(new ReferencedObjectUndeletableException(message));\r
+ result.addRelatedObject(ref);\r
+ result.setAbort();\r
+ }\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public IncludedTaxaDTO listIncludedTaxa(UUID taxonUuid, IncludedTaxonConfiguration config) {\r
+ IncludedTaxaDTO result = new IncludedTaxaDTO(taxonUuid);\r
+\r
+ //preliminary implementation\r
+\r
+ Set<Taxon> taxa = new HashSet<Taxon>();\r
+ TaxonBase taxonBase = find(taxonUuid);\r
+ if (taxonBase == null){\r
+ return new IncludedTaxaDTO();\r
+ }else if (taxonBase.isInstanceOf(Taxon.class)){\r
+ Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);\r
+ taxa.add(taxon);\r
+ }else if (taxonBase.isInstanceOf(Synonym.class)){\r
+ //TODO partial synonyms ??\r
+ //TODO synonyms in general\r
+ Synonym syn = CdmBase.deproxy(taxonBase, Synonym.class);\r
+ taxa.addAll(syn.getAcceptedTaxa());\r
+ }else{\r
+ throw new IllegalArgumentException("Unhandled class " + taxonBase.getClass().getSimpleName());\r
+ }\r
+\r
+ Set<Taxon> related = makeRelatedIncluded(taxa, result, config);\r
+ int i = 0;\r
+ while((! related.isEmpty()) && i++ < 100){ //to avoid\r
+ related = makeRelatedIncluded(related, result, config);\r
+ }\r
+\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Computes all children and conceptually congruent and included taxa and adds them to the existingTaxa\r
+ * data structure.\r
+ * @return the set of conceptually related taxa for further use\r
+ */\r
+ /**\r
+ * @param uncheckedTaxa\r
+ * @param existingTaxa\r
+ * @param config\r
+ * @return\r
+ */\r
+ private Set<Taxon> makeRelatedIncluded(Set<Taxon> uncheckedTaxa, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {\r
+\r
+ //children\r
+ Set<TaxonNode> taxonNodes = new HashSet<TaxonNode>();\r
+ for (Taxon taxon: uncheckedTaxa){\r
+ taxonNodes.addAll(taxon.getTaxonNodes());\r
+ }\r
+\r
+ Set<Taxon> children = new HashSet<Taxon>();\r
+ if (! config.onlyCongruent){\r
+ for (TaxonNode node: taxonNodes){\r
+ List<TaxonNode> childNodes = nodeService.loadChildNodesOfTaxonNode(node, null, true, false);\r
+ for (TaxonNode child : childNodes){\r
+ children.add(child.getTaxon());\r
+ }\r
+ }\r
+ children.remove(null); // just to be on the save side\r
+ }\r
+\r
+ Iterator<Taxon> it = children.iterator();\r
+ while(it.hasNext()){\r
+ UUID uuid = it.next().getUuid();\r
+ if (existingTaxa.contains(uuid)){\r
+ it.remove();\r
+ }else{\r
+ existingTaxa.addIncludedTaxon(uuid, new ArrayList<UUID>(), false);\r
+ }\r
+ }\r
+\r
+ //concept relations\r
+ Set<Taxon> uncheckedAndChildren = new HashSet<Taxon>(uncheckedTaxa);\r
+ uncheckedAndChildren.addAll(children);\r
+\r
+ Set<Taxon> relatedTaxa = makeConceptIncludedTaxa(uncheckedAndChildren, existingTaxa, config);\r
+\r
+\r
+ Set<Taxon> result = new HashSet<Taxon>(relatedTaxa);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Computes all conceptually congruent or included taxa and adds them to the existingTaxa data structure.\r
+ * @return the set of these computed taxa\r
+ */\r
+ private Set<Taxon> makeConceptIncludedTaxa(Set<Taxon> unchecked, IncludedTaxaDTO existingTaxa, IncludedTaxonConfiguration config) {\r
+ Set<Taxon> result = new HashSet<Taxon>();\r
+\r
+ for (Taxon taxon : unchecked){\r
+ Set<TaxonRelationship> fromRelations = taxon.getRelationsFromThisTaxon();\r
+ Set<TaxonRelationship> toRelations = taxon.getRelationsToThisTaxon();\r
+\r
+ for (TaxonRelationship fromRel : fromRelations){\r
+ if (config.includeDoubtful == false && fromRel.isDoubtful()){\r
+ continue;\r
+ }\r
+ if (fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO()) ||\r
+ !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.INCLUDES()) ||\r
+ !config.onlyCongruent && fromRel.getType().equals(TaxonRelationshipType.CONGRUENT_OR_INCLUDES())\r
+ ){\r
+ result.add(fromRel.getToTaxon());\r
+ }\r
+ }\r
+\r
+ for (TaxonRelationship toRel : toRelations){\r
+ if (config.includeDoubtful == false && toRel.isDoubtful()){\r
+ continue;\r
+ }\r
+ if (toRel.getType().equals(TaxonRelationshipType.CONGRUENT_TO())){\r
+ result.add(toRel.getFromTaxon());\r
+ }\r
+ }\r
+ }\r
+\r
+ Iterator<Taxon> it = result.iterator();\r
+ while(it.hasNext()){\r
+ UUID uuid = it.next().getUuid();\r
+ if (existingTaxa.contains(uuid)){\r
+ it.remove();\r
+ }else{\r
+ existingTaxa.addIncludedTaxon(uuid, new ArrayList<UUID>(), false);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public List<TaxonBase> findTaxaByName(MatchingTaxonConfigurator config){\r
+ List<TaxonBase> taxonList = dao.getTaxaByName(true, false, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, 0, config.getPropertyPath());\r
+ return taxonList;\r
+ }\r
+\r
+ @Override\r
+ @Transactional(readOnly = true)\r
+ public <S extends TaxonBase> Pager<FindByIdentifierDTO<S>> findByIdentifier(\r
+ Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,\r
+ MatchMode matchmode, boolean includeEntity, Integer pageSize,\r
+ Integer pageNumber, List<String> propertyPaths) {\r
+ if (subtreeFilter == null){\r
+ return findByIdentifier(clazz, identifier, identifierType, matchmode, includeEntity, pageSize, pageNumber, propertyPaths);\r
+ }\r
+\r
+ Integer numberOfResults = dao.countByIdentifier(clazz, identifier, identifierType, subtreeFilter, matchmode);\r
+ List<Object[]> daoResults = new ArrayList<Object[]>();\r
+ if(numberOfResults > 0) { // no point checking again\r
+ daoResults = dao.findByIdentifier(clazz, identifier, identifierType, subtreeFilter,\r
+ matchmode, includeEntity, pageSize, pageNumber, propertyPaths);\r
+ }\r
+\r
+ List<FindByIdentifierDTO<S>> result = new ArrayList<FindByIdentifierDTO<S>>();\r
+ for (Object[] daoObj : daoResults){\r
+ if (includeEntity){\r
+ result.add(new FindByIdentifierDTO<S>((DefinedTerm)daoObj[0], (String)daoObj[1], (S)daoObj[2]));\r
+ }else{\r
+ result.add(new FindByIdentifierDTO<S>((DefinedTerm)daoObj[0], (String)daoObj[1], (UUID)daoObj[2], (String)daoObj[3]));\r
+ }\r
+ }\r
+ return new DefaultPagerImpl<FindByIdentifierDTO<S>>(pageNumber, numberOfResults, pageSize, result);\r
+ }\r
+\r
+\r