Changeset 7895
- Timestamp:
- Jan 18, 2021, 11:46:25 AM (2 years ago)
- Location:
- trunk/src
- Files:
-
- 1 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/clients/web/net/sf/basedb/clients/web/Base.java
r7890 r7895 74 74 import net.sf.basedb.util.ColorGenerator; 75 75 import net.sf.basedb.util.Values; 76 import net.sf.basedb.util.extensions.ClientContext; 76 77 import net.sf.basedb.util.filter.Filter; 77 78 import net.sf.basedb.util.filter.StaticFilter; … … 1014 1015 1015 1016 /** 1017 @deprecated In 3.18, use {@link #getConfiguredQuery(DbControl, ItemContext, ClientContext, boolean, ItemQuery, ModeInfo)} instead 1018 */ 1019 @Deprecated 1020 public static <T extends BasicItem> ItemQuery<T> getConfiguredQuery(DbControl dc, ItemContext cc, boolean autoLeftJoin, ItemQuery<T> query, ModeInfo mode) 1021 { 1022 return getConfiguredQuery(dc, cc, null, autoLeftJoin, query, mode); 1023 } 1024 1025 /** 1016 1026 Configures a query. This method calls {@link ItemContext#configureQuery(DbControl, EntityQuery, boolean)} 1017 1027 and then adds the following filter: … … 1029 1039 @param mode Info about the mode the page has. 1030 1040 @return The same query, configured according to information in the current context 1031 @since 2.81032 */ 1033 public static <T extends BasicItem> ItemQuery<T> getConfiguredQuery(DbControl dc, ItemContext cc, boolean autoLeftJoin, ItemQuery<T> query, ModeInfo mode)1034 { 1035 cc.configureQuery(dc, query, autoLeftJoin);1041 @since 3.18 1042 */ 1043 public static <T extends BasicItem> ItemQuery<T> getConfiguredQuery(DbControl dc, ItemContext cc, ClientContext context, boolean autoLeftJoin, ItemQuery<T> query, ModeInfo mode) 1044 { 1045 cc.configureQuery(dc, query, context, autoLeftJoin); 1036 1046 final String exclude = (String)cc.getObject("exclude"); 1037 1047 if (exclude != null) -
trunk/src/core/core-extensions.xml
r7535 r7895 167 167 </extension-point> 168 168 169 <extension-point 170 id="net.sf.basedb.query.entity-query-filter"> 171 <action-class>net.sf.basedb.core.query.QueryFilterAction</action-class> 172 <name>Query filters</name> 173 <description> 174 Extension point for implementing extra filter functionality for 175 queries generated by the ItemContext.configureQuery() method. 176 If the call is initiated from the web client the client context is 177 a JspContext, otherwise it is a ClientContext instance. In both cases 178 the current item is the ItemContext instance. The ItemContext.getQuery() 179 method returns the query that was passed to the configureQuery() method. 180 Factory implementations are recommended to check ItemContext.hasExtensionFilter() 181 before returning actions, and QueryFilterAction implementations should check 182 PropertyFilter.isExtensionFilter(). 183 </description> 184 </extension-point> 185 169 186 <extension 170 187 id="net.sf.basedb.core.uri.http-connection-manager" -
trunk/src/core/net/sf/basedb/core/ItemContext.java
r7813 r7895 24 24 25 25 import net.sf.basedb.core.query.Annotations; 26 import net.sf.basedb.core.query.QueryFilterAction; 26 27 import net.sf.basedb.core.query.EntityQuery; 27 28 import net.sf.basedb.core.query.Expressions; … … 47 48 import net.sf.basedb.util.encode.EncodeUtil; 48 49 import net.sf.basedb.util.encode.TabCrLfEncoderDecoder; 50 import net.sf.basedb.util.extensions.ClientContext; 51 import net.sf.basedb.util.extensions.ExtensionsInvoker; 52 import net.sf.basedb.util.extensions.manager.ExtensionsManager; 49 53 import net.sf.basedb.util.filter.Filter; 50 54 import net.sf.basedb.util.jep.Jep; … … 814 818 815 819 /** 820 Checks if there is an extension filter with a property 821 starting with !x.prefix. 822 @param prefix The extension prefix to check, or null to check any extension 823 @since 3.18 824 */ 825 public boolean hasExtensionFilter(String prefix) 826 { 827 if (propertyFilters == null) return false; 828 prefix = "!x." + (prefix == null ? "" : prefix); 829 for (PropertyFilterPair filterPair : propertyFilters.values()) 830 { 831 if (filterPair.getEffectiveFilter().getProperty().startsWith(prefix)) return true; 832 } 833 return false; 834 835 } 836 837 /** 816 838 Get the value of a property filter on row 0. 817 839 @see #getPropertyValue(String, int) … … 1519 1541 Arrays.asList("£", "&", "!", "¤", "|", "/")); 1520 1542 1543 public void configureQuery(DbControl dc, EntityQuery query, boolean autoLeftJoin) 1544 throws BaseException 1545 { 1546 configureQuery(dc, query, null, autoLeftJoin); 1547 } 1548 1521 1549 /** 1522 1550 Use the settings in this context to configure a query. … … 1543 1571 @param dc The DbControl that will be used to execute the query 1544 1572 @param query The query to configure 1573 @param context Optional ClientContext for filter extensions (if null, a context is automatically created if needed) 1545 1574 @param autoLeftJoin If true, and the sort property and filters are checked 1546 1575 for associations to other items, which are automatically left joined to … … 1548 1577 is experimental) 1549 1578 @throws BaseException If configuring the query fails. 1550 @since 2.81551 */ 1552 public void configureQuery(DbControl dc, EntityQuery query, boolean autoLeftJoin)1579 @since 3.18 1580 */ 1581 public void configureQuery(DbControl dc, EntityQuery query, ClientContext context, boolean autoLeftJoin) 1553 1582 throws BaseException 1554 1583 { … … 1679 1708 if (permission != null) query.setItemPermission(permission); 1680 1709 1710 // Initialise query filter extensions 1711 if (context == null) context = new ClientContext(dc); 1712 context.setCurrentItem(this); 1713 setQuery(query); 1714 ExtensionsManager manager = Application.getExtensionsManager(); 1715 List<QueryFilterAction> xtFilter = new ArrayList<>(); 1716 ExtensionsInvoker<QueryFilterAction> invoker = manager.getRegistry().useExtensions(context, 1717 manager.getSettings(), "net.sf.basedb.query.entity-query-filter"); 1718 invoker.forEach(xtFilter::add); 1719 1681 1720 // Add property filters 1721 QueryRestrictions restrictions = new QueryRestrictions(); 1682 1722 if (propertyFilters != null) 1683 1723 { 1684 Map<Integer, List<Restriction>> allRestrictions = new HashMap<Integer, List<Restriction>>(); 1685 Map<Integer, IdListRestriction> rowIdListRestriction = new HashMap<Integer, IdListRestriction>(); 1724 // 1. Filters per column/row 1686 1725 for (PropertyFilterPair filterPair : propertyFilters.values()) 1687 1726 { … … 1689 1728 if (propertyInspector == null || propertyInspector.evaluate(filter.getProperty())) 1690 1729 { 1730 int rowIndex = filter.getRowIndex(); 1731 RowRestrictions rowRestrictions = restrictions.getRow(rowIndex); 1732 Restriction r = null; 1691 1733 try 1692 1734 { 1693 Restriction r = filter.getRestriction(dc, query); 1694 int rowIndex = filter.getRowIndex(); 1695 if (r instanceof IdListRestriction) 1735 // Get restriction from extension 1736 for (QueryFilterAction cfa : xtFilter) 1696 1737 { 1697 // Filters that result in a "IdListRestriction" are merged to a single id-list 1698 // since that typically will result in a shorter list in the final SQL 1699 IdListRestriction rowIdR = rowIdListRestriction.get(rowIndex); 1700 if (rowIdR == null) 1701 { 1702 rowIdListRestriction.put(rowIndex, (IdListRestriction)r); 1703 } 1704 else 1705 { 1706 rowIdR.intersect((IdListRestriction)r); 1707 r = null; 1708 } 1738 r = cfa.getColumnRestriction(filter); 1739 if (r != null) break; 1709 1740 } 1710 if (r != null) 1711 { 1712 List<Restriction> rowRestrictions = allRestrictions.get(rowIndex); 1713 if (rowRestrictions == null) 1714 { 1715 rowRestrictions = new ArrayList<Restriction>(); 1716 allRestrictions.put(rowIndex, rowRestrictions); 1717 } 1718 rowRestrictions.add(r); 1719 // left join if filter property is association 1720 if (autoLeftJoin) 1721 { 1722 String filterProperty = filter.getProperty(); 1723 if (filterProperty != null && !NO_AUTO_JOIN_PREFIXES.contains(filterProperty.substring(0, 1))) 1724 { 1725 if (filterProperty.startsWith("#") && filterProperty.contains("(")) 1726 { 1727 int joinStart = filterProperty.indexOf('('); 1728 int joinEnd = filterProperty.indexOf(')'); 1729 filterProperty = filterProperty.substring(joinStart+1, joinEnd)+".foobar"; // 'foobar' is removed in the actual join 1730 } 1731 1732 int lastDotIndex = filterProperty.lastIndexOf('.'); 1733 int firstDotIndex = filterProperty.indexOf('.'); 1734 if (lastDotIndex >= 0) 1735 { 1736 filterProperty = filterProperty.substring(0, lastDotIndex); 1737 if (!filterProperty.startsWith("$") || lastDotIndex != firstDotIndex) 1738 { 1739 leftJoins.add(filterProperty); 1740 } 1741 } 1742 } 1743 } 1744 } 1741 1742 // Get default restriction 1743 if (r == null) r = filter.getRestriction(dc, query); 1745 1744 } 1746 1745 catch (Throwable ex) … … 1750 1749 setMessage(msg + ": " + ex.getMessage()); 1751 1750 } 1751 1752 if (r != null) rowRestrictions.addRestriction(r); 1753 1754 // left join if filter property is association 1755 if (autoLeftJoin && r != null) 1756 { 1757 String filterProperty = filter.getProperty(); 1758 if (filterProperty != null && !NO_AUTO_JOIN_PREFIXES.contains(filterProperty.substring(0, 1))) 1759 { 1760 if (filterProperty.startsWith("#") && filterProperty.contains("(")) 1761 { 1762 int joinStart = filterProperty.indexOf('('); 1763 int joinEnd = filterProperty.indexOf(')'); 1764 filterProperty = filterProperty.substring(joinStart+1, joinEnd)+".foobar"; // 'foobar' is removed in the actual join 1765 } 1766 1767 int lastDotIndex = filterProperty.lastIndexOf('.'); 1768 int firstDotIndex = filterProperty.indexOf('.'); 1769 if (lastDotIndex >= 0) 1770 { 1771 filterProperty = filterProperty.substring(0, lastDotIndex); 1772 if (!filterProperty.startsWith("$") || lastDotIndex != firstDotIndex) 1773 { 1774 leftJoins.add(filterProperty); 1775 } 1776 } 1777 } 1778 } 1752 1779 } 1753 1780 } 1754 if (allRestrictions.size() > 0)1755 {1756 Restriction[] rows = new Restriction[allRestrictions.size()];1757 int rowNo = 0;1758 for ( List<Restriction> rowRestrictions : allRestrictions.values())1781 1782 // 2. Let extensions create extra restrictions per row if they want to 1783 for (RowRestrictions row : restrictions.rowRestrictions.values()) 1784 { 1785 for (QueryFilterAction act : xtFilter) 1759 1786 { 1760 rows[rowNo] = Restrictions.nullSafeAnd(rowRestrictions);1761 rowNo++;1787 Restriction r = act.getRowRestriction(row.rowIndex); 1788 if (r != null) row.addRestriction(r); 1762 1789 } 1763 query.restrict(Restrictions.or(rows)); 1764 } 1790 } 1791 1792 // 3. The final restriction from filters is added to the query 1793 Restriction finalRestriction = restrictions.getRestriction(); 1794 if (finalRestriction != null) query.restrict(finalRestriction); 1795 } 1796 1797 // 4. Let extensions create extra restrictions if they want to 1798 for (QueryFilterAction act : xtFilter) 1799 { 1800 Restriction r = act.getQueryRestriction(); 1801 if (r != null) query.restrict(r); 1765 1802 } 1766 1803 … … 2288 2325 } 2289 2326 2327 2328 static class QueryRestrictions 2329 { 2330 Map<Integer, RowRestrictions> rowRestrictions = new HashMap<>(); 2331 2332 Restriction getRestriction() 2333 { 2334 List<Restriction> rows = new ArrayList<Restriction>(rowRestrictions.size()); 2335 for (RowRestrictions row : rowRestrictions.values()) 2336 { 2337 rows.add(row.getRowRestriction()); 2338 } 2339 return Restrictions.nullSafeOr(rows); 2340 } 2341 2342 RowRestrictions getRow(int rowIndex) 2343 { 2344 RowRestrictions row = rowRestrictions.get(rowIndex); 2345 if (row == null) 2346 { 2347 row = new RowRestrictions(rowIndex); 2348 rowRestrictions.put(rowIndex, row); 2349 } 2350 return row; 2351 } 2352 } 2353 2354 2355 static class RowRestrictions 2356 { 2357 final int rowIndex; 2358 2359 List<Restriction> restrictions = new ArrayList<>(); 2360 IdListRestriction rowIdList = null; 2361 2362 public RowRestrictions(int rowIndex) 2363 { 2364 this.rowIndex = rowIndex; 2365 } 2366 2367 Restriction getRowRestriction() 2368 { 2369 return Restrictions.nullSafeAnd(restrictions); 2370 } 2371 2372 void addRestriction(Restriction r) 2373 { 2374 if (r instanceof IdListRestriction) 2375 { 2376 if (rowIdList != null) 2377 { 2378 rowIdList.intersect((IdListRestriction)r); 2379 r = null; 2380 } 2381 else 2382 { 2383 rowIdList = (IdListRestriction)r; 2384 } 2385 } 2386 if (r != null) restrictions.add(r); 2387 } 2388 } 2389 2290 2390 } -
trunk/src/core/net/sf/basedb/core/PropertyFilter.java
r7843 r7895 240 240 { 241 241 return property; 242 } 243 244 /** 245 Checks if this filter is an extension filter. An extension 246 filter has a {@link #getProperty()} that starts with 247 "!x.prefix". 248 @param prefix The extension prefix to check, or null to check any extension 249 @since 3.18 250 */ 251 public boolean isExtensionFilter(String prefix) 252 { 253 return prefix == null ? property.startsWith("!x.") : property.startsWith("!x."+prefix); 242 254 } 243 255 … … 532 544 Eg. &experiments(name). The filter is applied as a subquery. 533 545 <p> 534 If the property starts with ! it is another special filter. There is currently 535 one such filter (since BASE 2.15): !sharedTo.name, which is used to filter 536 shareable items by the name of users/groups/projects it has been shared to. 537 The 'name' part can be replaced with any other property that is common for 538 users, groups and projects (eg. description or id). 546 If the property starts with ! it is another special filter: 547 <ul> 548 <li>Since BASE 2.15: !sharedTo.name, which is used to filter 549 shareable items by the name of users/groups/projects it has been shared to. 550 The 'name' part can be replaced with any other property that is common for 551 users, groups and projects (eg. description or id). 552 <li>Since BASE 3.18: !x., which is intended to be handled by extensions. It 553 is ignored by the BASE core code. The rest of the property should be a unique 554 identifier for the extension that is handling the filter. 539 555 <p> 540 556 The property may also start with $@. … … 549 565 String alias = null; 550 566 Expression indexExpression = null; 567 568 // Ignore -- expected to be handled by extensions 569 if (property != null && property.startsWith("!x.")) return null; 551 570 552 571 if (property != null && property.startsWith("$")) -
trunk/src/core/net/sf/basedb/util/extensions/ClientContext.java
r5614 r7895 160 160 @return The current item, or null 161 161 */ 162 public Object getCurrentItem() 163 { 164 return item; 162 @SuppressWarnings("unchecked") 163 public <T> T getCurrentItem() 164 { 165 return (T)item; 165 166 } 166 167 … … 179 180 @return The value, or null 180 181 */ 181 public Object getAttribute(String name) 182 { 183 return attributes == null ? null : attributes.get(name); 182 @SuppressWarnings("unchecked") 183 public <T> T getAttribute(String name) 184 { 185 return attributes == null ? null : (T)attributes.get(name); 184 186 } 185 187
Note: See TracChangeset
for help on using the changeset viewer.