1 /* 2 * Created on Sep 4, 2013 3 * Created by Paul Gardner 4 * 5 * Copyright 2013 Azureus Software, Inc. All rights reserved. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 */ 19 20 21 package com.aelitis.azureus.core.tag.impl; 22 23 import java.util.*; 24 import java.util.regex.Pattern; 25 26 import org.gudy.azureus2.core3.disk.DiskManager; 27 import org.gudy.azureus2.core3.download.DownloadManager; 28 import org.gudy.azureus2.core3.download.DownloadManagerState; 29 import org.gudy.azureus2.core3.torrent.TOTorrent; 30 import org.gudy.azureus2.core3.tracker.client.TRTrackerScraperResponse; 31 import org.gudy.azureus2.core3.util.AENetworkClassifier; 32 import org.gudy.azureus2.core3.util.AERunnable; 33 import org.gudy.azureus2.core3.util.AsyncDispatcher; 34 import org.gudy.azureus2.core3.util.Debug; 35 import org.gudy.azureus2.core3.util.FrequencyLimitedDispatcher; 36 import org.gudy.azureus2.core3.util.SimpleTimer; 37 import org.gudy.azureus2.core3.util.SystemTime; 38 import org.gudy.azureus2.core3.util.TimerEvent; 39 import org.gudy.azureus2.core3.util.TimerEventPerformer; 40 import org.gudy.azureus2.core3.util.TimerEventPeriodic; 41 import org.gudy.azureus2.plugins.download.Download; 42 import org.gudy.azureus2.plugins.download.DownloadListener; 43 import org.gudy.azureus2.pluginsimpl.local.PluginCoreUtils; 44 45 import com.aelitis.azureus.core.AzureusCore; 46 import com.aelitis.azureus.core.AzureusCoreFactory; 47 import com.aelitis.azureus.core.AzureusCoreRunningListener; 48 import com.aelitis.azureus.core.tag.Tag; 49 import com.aelitis.azureus.core.tag.TagFeatureProperties; 50 import com.aelitis.azureus.core.tag.TagFeatureProperties.TagProperty; 51 import com.aelitis.azureus.core.tag.TagFeatureProperties.TagPropertyListener; 52 import com.aelitis.azureus.core.tag.TagListener; 53 import com.aelitis.azureus.core.tag.TagType; 54 import com.aelitis.azureus.core.tag.TagTypeListener; 55 import com.aelitis.azureus.core.tag.Taggable; 56 import com.aelitis.azureus.core.tag.TaggableLifecycleAdapter; 57 58 public class 59 TagPropertyConstraintHandler 60 implements TagTypeListener, DownloadListener 61 { 62 private final AzureusCore azureus_core; 63 private final TagManagerImpl tag_manager; 64 65 private boolean initialised; 66 private boolean initial_assignment_complete; 67 68 private Map<Tag,TagConstraint> constrained_tags = new HashMap<Tag,TagConstraint>(); 69 70 private boolean dm_listener_added; 71 72 private Map<Tag,Map<DownloadManager,Long>> apply_history = new HashMap<Tag, Map<DownloadManager,Long>>(); 73 74 private AsyncDispatcher dispatcher = new AsyncDispatcher( "tag:constraints" ); 75 76 private FrequencyLimitedDispatcher freq_lim_dispatcher = 77 new FrequencyLimitedDispatcher( 78 new AERunnable() 79 { 80 public void 81 runSupport() 82 { 83 checkFreqLimUpdates(); 84 } 85 }, 86 5000 ); 87 88 private IdentityHashMap<DownloadManager,List<TagConstraint>> freq_lim_pending = new IdentityHashMap<DownloadManager,List<TagConstraint>>(); 89 90 91 private TimerEventPeriodic timer; 92 93 private TagPropertyConstraintHandler()94 TagPropertyConstraintHandler() 95 { 96 azureus_core = null; 97 tag_manager = null; 98 } 99 100 protected TagPropertyConstraintHandler( AzureusCore _core, TagManagerImpl _tm )101 TagPropertyConstraintHandler( 102 AzureusCore _core, 103 TagManagerImpl _tm ) 104 { 105 azureus_core = _core; 106 tag_manager = _tm; 107 108 tag_manager.addTaggableLifecycleListener( 109 Taggable.TT_DOWNLOAD, 110 new TaggableLifecycleAdapter() 111 { 112 public void 113 initialised( 114 List<Taggable> current_taggables ) 115 { 116 try{ 117 TagType tt = tag_manager.getTagType( TagType.TT_DOWNLOAD_MANUAL ); 118 119 tt.addTagTypeListener( TagPropertyConstraintHandler.this, true ); 120 121 }finally{ 122 123 AzureusCoreFactory.addCoreRunningListener( 124 new AzureusCoreRunningListener() 125 { 126 public void 127 azureusCoreRunning( 128 AzureusCore core ) 129 { 130 synchronized( constrained_tags ){ 131 132 initialised = true; 133 134 apply( core.getGlobalManager().getDownloadManagers(), true ); 135 } 136 } 137 }); 138 } 139 } 140 141 public void 142 taggableCreated( 143 Taggable taggable ) 144 { 145 apply((DownloadManager)taggable, null, false ); 146 } 147 }); 148 } 149 150 public void tagTypeChanged( TagType tag_type )151 tagTypeChanged( 152 TagType tag_type ) 153 { 154 } 155 156 public void tagAdded( Tag tag )157 tagAdded( 158 Tag tag ) 159 { 160 TagFeatureProperties tfp = (TagFeatureProperties)tag; 161 162 TagProperty prop = tfp.getProperty( TagFeatureProperties.PR_CONSTRAINT ); 163 164 if ( prop != null ){ 165 166 prop.addListener( 167 new TagPropertyListener() 168 { 169 public void 170 propertyChanged( 171 TagProperty property ) 172 { 173 handleProperty( property ); 174 } 175 176 public void 177 propertySync( 178 TagProperty property ) 179 { 180 } 181 }); 182 183 handleProperty( prop ); 184 } 185 186 tag.addTagListener( 187 new TagListener() 188 { 189 public void 190 taggableSync( 191 Tag tag ) 192 { 193 } 194 195 public void 196 taggableRemoved( 197 Tag tag, 198 Taggable tagged ) 199 { 200 apply((DownloadManager)tagged, tag, true ); 201 } 202 203 public void 204 taggableAdded( 205 Tag tag, 206 Taggable tagged ) 207 { 208 apply((DownloadManager)tagged, tag, true ); 209 } 210 }, false ); 211 } 212 213 public void tagChanged( Tag tag )214 tagChanged( 215 Tag tag ) 216 { 217 } 218 219 private void checkTimer()220 checkTimer() 221 { 222 // already synchronized on constrainted_tags by callers 223 224 if ( constrained_tags.size() > 0 ){ 225 226 if ( timer == null ){ 227 228 timer = 229 SimpleTimer.addPeriodicEvent( 230 "tag:constraint:timer", 231 30*1000, 232 new TimerEventPerformer() { 233 234 public void 235 perform( 236 TimerEvent event) 237 { 238 apply_history.clear(); 239 240 apply(); 241 } 242 }); 243 244 AzureusCoreFactory.addCoreRunningListener( 245 new AzureusCoreRunningListener() 246 { 247 public void 248 azureusCoreRunning( 249 AzureusCore core ) 250 { 251 synchronized( constrained_tags ){ 252 253 if ( timer != null ){ 254 255 azureus_core.getPluginManager().getDefaultPluginInterface().getDownloadManager().getGlobalDownloadEventNotifier().addListener( TagPropertyConstraintHandler.this ); 256 257 dm_listener_added = true; 258 } 259 } 260 } 261 }); 262 } 263 264 }else if ( timer != null ){ 265 266 timer.cancel(); 267 268 timer = null; 269 270 if ( dm_listener_added ){ 271 272 azureus_core.getPluginManager().getDefaultPluginInterface().getDownloadManager().getGlobalDownloadEventNotifier().removeListener( this ); 273 } 274 275 apply_history.clear(); 276 } 277 } 278 279 private void checkFreqLimUpdates()280 checkFreqLimUpdates() 281 { 282 dispatcher.dispatch( 283 new AERunnable() 284 { 285 public void 286 runSupport() 287 { 288 synchronized( freq_lim_pending ){ 289 290 for ( Map.Entry<DownloadManager,List<TagConstraint>> entry: freq_lim_pending.entrySet()){ 291 292 for ( TagConstraint con: entry.getValue()){ 293 294 con.apply( entry.getKey()); 295 } 296 } 297 298 freq_lim_pending.clear(); 299 } 300 } 301 }); 302 } 303 304 public void stateChanged( Download download, int old_state, int new_state )305 stateChanged( 306 Download download, 307 int old_state, 308 int new_state ) 309 { 310 List<TagConstraint> interesting = new ArrayList<TagConstraint>(); 311 312 synchronized( constrained_tags ){ 313 314 if ( !initialised ){ 315 316 return; 317 } 318 319 for ( TagConstraint tc: constrained_tags.values()){ 320 321 if ( tc.dependOnDownloadState()){ 322 323 interesting.add( tc ); 324 } 325 } 326 } 327 328 if ( interesting.size() > 0 ){ 329 330 DownloadManager dm = PluginCoreUtils.unwrap( download ); 331 332 synchronized( freq_lim_pending ){ 333 334 freq_lim_pending.put( dm, interesting ); 335 } 336 337 freq_lim_dispatcher.dispatch(); 338 } 339 } 340 341 public void positionChanged( Download download, int oldPosition, int newPosition )342 positionChanged( 343 Download download, 344 int oldPosition, 345 int newPosition ) 346 { 347 } 348 349 public void tagRemoved( Tag tag )350 tagRemoved( 351 Tag tag ) 352 { 353 synchronized( constrained_tags ){ 354 355 if ( constrained_tags.containsKey( tag )){ 356 357 constrained_tags.remove( tag ); 358 359 checkTimer(); 360 } 361 } 362 } 363 364 private void handleProperty( TagProperty property )365 handleProperty( 366 TagProperty property ) 367 { 368 Tag tag = property.getTag(); 369 370 synchronized( constrained_tags ){ 371 372 String[] value = property.getStringList(); 373 374 String constraint; 375 String options; 376 377 if ( value == null ){ 378 379 constraint = ""; 380 options = ""; 381 382 }else{ 383 384 constraint = value.length>0&&value[0]!=null?value[0].trim():""; 385 options = value.length>1&&value[1]!=null?value[1].trim():""; 386 } 387 388 if ( constraint.length() == 0 ){ 389 390 if ( constrained_tags.containsKey( tag )){ 391 392 constrained_tags.remove( tag ); 393 } 394 }else{ 395 396 TagConstraint con = constrained_tags.get( tag ); 397 398 if ( con != null && con.getConstraint().equals( constraint ) && con.getOptions().equals( options )){ 399 400 return; 401 } 402 403 Set<Taggable> existing = tag.getTagged(); 404 405 for ( Taggable e: existing ){ 406 407 tag.removeTaggable( e ); 408 } 409 410 con = new TagConstraint( this, tag, constraint, options ); 411 412 constrained_tags.put( tag, con ); 413 414 if ( initialised ){ 415 416 apply( con ); 417 } 418 } 419 420 checkTimer(); 421 } 422 } 423 424 private void apply( final DownloadManager dm, Tag related_tag, boolean auto )425 apply( 426 final DownloadManager dm, 427 Tag related_tag, 428 boolean auto ) 429 { 430 if ( dm.isDestroyed()){ 431 432 return; 433 } 434 435 synchronized( constrained_tags ){ 436 437 if ( constrained_tags.size() == 0 || !initialised ){ 438 439 return; 440 } 441 442 if ( auto && !initial_assignment_complete ){ 443 444 return; 445 } 446 } 447 448 dispatcher.dispatch( 449 new AERunnable() 450 { 451 public void 452 runSupport() 453 { 454 List<TagConstraint> cons; 455 456 synchronized( constrained_tags ){ 457 458 cons = new ArrayList<TagConstraint>( constrained_tags.values()); 459 } 460 461 for ( TagConstraint con: cons ){ 462 463 con.apply( dm ); 464 } 465 } 466 }); 467 } 468 469 private void apply( final List<DownloadManager> dms, final boolean initial_assignment )470 apply( 471 final List<DownloadManager> dms, 472 final boolean initial_assignment ) 473 { 474 synchronized( constrained_tags ){ 475 476 if ( constrained_tags.size() == 0 || !initialised ){ 477 478 return; 479 } 480 } 481 482 dispatcher.dispatch( 483 new AERunnable() 484 { 485 public void 486 runSupport() 487 { 488 List<TagConstraint> cons; 489 490 synchronized( constrained_tags ){ 491 492 cons = new ArrayList<TagConstraint>( constrained_tags.values()); 493 } 494 495 // set up initial constraint tagged state without following implications 496 497 for ( TagConstraint con: cons ){ 498 499 con.apply( dms ); 500 } 501 502 if ( initial_assignment ){ 503 504 synchronized( constrained_tags ){ 505 506 initial_assignment_complete = true; 507 } 508 509 // go over them one more time to pick up consequential constraints 510 511 for ( TagConstraint con: cons ){ 512 513 con.apply( dms ); 514 } 515 } 516 } 517 }); 518 } 519 520 private void apply( final TagConstraint constraint )521 apply( 522 final TagConstraint constraint ) 523 { 524 synchronized( constrained_tags ){ 525 526 if ( !initialised ){ 527 528 return; 529 } 530 } 531 532 dispatcher.dispatch( 533 new AERunnable() 534 { 535 public void 536 runSupport() 537 { 538 List<DownloadManager> dms = azureus_core.getGlobalManager().getDownloadManagers(); 539 540 constraint.apply( dms ); 541 } 542 }); 543 } 544 545 private void apply()546 apply() 547 { 548 synchronized( constrained_tags ){ 549 550 if ( constrained_tags.size() == 0 || !initialised ){ 551 552 return; 553 } 554 } 555 556 dispatcher.dispatch( 557 new AERunnable() 558 { 559 public void 560 runSupport() 561 { 562 List<DownloadManager> dms = azureus_core.getGlobalManager().getDownloadManagers(); 563 564 List<TagConstraint> cons; 565 566 synchronized( constrained_tags ){ 567 568 cons = new ArrayList<TagConstraint>( constrained_tags.values()); 569 } 570 571 for ( TagConstraint con: cons ){ 572 573 con.apply( dms ); 574 } 575 } 576 }); 577 } 578 579 private TagConstraint.ConstraintExpr compileConstraint( String expr )580 compileConstraint( 581 String expr ) 582 { 583 return( new TagConstraint( this, null, expr, null ).expr ); 584 } 585 586 private static class 587 TagConstraint 588 { 589 private final TagPropertyConstraintHandler handler; 590 private final Tag tag; 591 private final String constraint; 592 593 private final boolean auto_add; 594 private final boolean auto_remove; 595 596 private final ConstraintExpr expr; 597 598 private boolean depends_on_download_state; 599 600 private TagConstraint( TagPropertyConstraintHandler _handler, Tag _tag, String _constraint, String options )601 TagConstraint( 602 TagPropertyConstraintHandler _handler, 603 Tag _tag, 604 String _constraint, 605 String options ) 606 { 607 handler = _handler; 608 tag = _tag; 609 constraint = _constraint; 610 611 if ( options == null ){ 612 613 auto_add = true; 614 auto_remove = true; 615 616 }else{ 617 // 0 = add+remove; 1 = add only; 2 = remove only 618 619 auto_add = !options.contains( "am=2;" ); 620 auto_remove = !options.contains( "am=1;" ); 621 } 622 623 ConstraintExpr compiled_expr = null; 624 625 try{ 626 compiled_expr = compileStart( constraint, new HashMap<String,ConstraintExpr>()); 627 628 }catch( Throwable e ){ 629 630 Debug.out( "Invalid constraint: " + constraint + " - " + Debug.getNestedExceptionMessage( e )); 631 632 }finally{ 633 634 expr = compiled_expr; 635 } 636 } 637 638 private boolean dependOnDownloadState()639 dependOnDownloadState() 640 { 641 return( depends_on_download_state ); 642 } 643 644 private ConstraintExpr compileStart( String str, Map<String,ConstraintExpr> context )645 compileStart( 646 String str, 647 Map<String,ConstraintExpr> context ) 648 { 649 str = str.trim(); 650 651 if ( str.equalsIgnoreCase( "true" )){ 652 653 return( new ConstraintExprTrue()); 654 } 655 656 char[] chars = str.toCharArray(); 657 658 boolean in_quote = false; 659 660 int level = 0; 661 int bracket_start = 0; 662 663 StringBuffer result = new StringBuffer( str.length()); 664 665 for ( int i=0;i<chars.length;i++){ 666 667 char c = chars[i]; 668 669 if ( c == '"' ){ 670 671 if ( i == 0 || chars[i-1] != '\\' ){ 672 673 in_quote = !in_quote; 674 } 675 } 676 677 if ( !in_quote ){ 678 679 if ( c == '(' ){ 680 681 level++; 682 683 if ( level == 1 ){ 684 685 bracket_start = i+1; 686 } 687 }else if ( c == ')' ){ 688 689 level--; 690 691 if ( level == 0 ){ 692 693 String bracket_text = new String( chars, bracket_start, i-bracket_start ).trim(); 694 695 if ( result.length() > 0 && Character.isLetterOrDigit( result.charAt( result.length()-1 ))){ 696 697 // function call 698 699 String key = "{" + context.size() + "}"; 700 701 context.put( key, new ConstraintExprParams( bracket_text )); 702 703 result.append( "(" ).append( key ).append( ")" ); 704 705 }else{ 706 707 ConstraintExpr sub_expr = compileStart( bracket_text, context ); 708 709 String key = "{" + context.size() + "}"; 710 711 context.put(key, sub_expr ); 712 713 result.append( key ); 714 } 715 } 716 }else if ( level == 0 ){ 717 718 if ( !Character.isWhitespace( c )){ 719 720 result.append( c ); 721 } 722 } 723 }else if ( level == 0 ){ 724 725 result.append( c ); 726 727 } 728 } 729 730 if ( level != 0 ){ 731 732 throw( new RuntimeException( "Unmatched '(' in \"" + str + "\"" )); 733 } 734 735 if ( in_quote ){ 736 737 throw( new RuntimeException( "Unmatched '\"' in \"" + str + "\"" )); 738 } 739 740 return( compileBasic( result.toString(), context )); 741 } 742 743 private ConstraintExpr compileBasic( String str, Map<String,ConstraintExpr> context )744 compileBasic( 745 String str, 746 Map<String,ConstraintExpr> context ) 747 { 748 if ( str.startsWith( "{" )){ 749 750 return( context.get( str )); 751 752 }else if ( str.contains( "||" )){ 753 754 String[] bits = str.split( "\\|\\|" ); 755 756 return( new ConstraintExprOr( compile( bits, context ))); 757 758 }else if ( str.contains( "&&" )){ 759 760 String[] bits = str.split( "&&" ); 761 762 return( new ConstraintExprAnd( compile( bits, context ))); 763 764 }else if ( str.contains( "^" )){ 765 766 String[] bits = str.split( "\\^" ); 767 768 return( new ConstraintExprXor( compile( bits, context ))); 769 770 }else if ( str.startsWith( "!" )){ 771 772 return( new ConstraintExprNot( compileBasic( str.substring(1).trim(), context ))); 773 774 }else{ 775 776 int pos = str.indexOf( '(' ); 777 778 if ( pos > 0 && str.endsWith( ")" )){ 779 780 String func = str.substring( 0, pos ); 781 782 String key = str.substring( pos+1, str.length() - 1 ).trim(); 783 784 ConstraintExprParams params = (ConstraintExprParams)context.get( key ); 785 786 return( new ConstraintExprFunction( func, params )); 787 788 }else{ 789 790 throw( new RuntimeException( "Unsupported construct: " + str )); 791 } 792 } 793 } 794 795 private ConstraintExpr[] compile( String[] bits, Map<String,ConstraintExpr> context )796 compile( 797 String[] bits, 798 Map<String,ConstraintExpr> context ) 799 { 800 ConstraintExpr[] res = new ConstraintExpr[ bits.length ]; 801 802 for ( int i=0; i<bits.length;i++){ 803 804 res[i] = compileBasic( bits[i].trim(), context ); 805 } 806 807 return( res ); 808 } 809 810 private Tag getTag()811 getTag() 812 { 813 return( tag ); 814 } 815 816 private String getConstraint()817 getConstraint() 818 { 819 return( constraint ); 820 } 821 822 private String getOptions()823 getOptions() 824 { 825 if ( auto_add ){ 826 return( "am=1;" ); 827 }else if ( auto_remove ){ 828 return( "am=2;" ); 829 }else{ 830 return( "am=0;" ); 831 } 832 } 833 834 private void apply( DownloadManager dm )835 apply( 836 DownloadManager dm ) 837 { 838 if ( dm.isDestroyed() || !dm.isPersistent()){ 839 840 return; 841 } 842 843 if ( expr == null ){ 844 845 return; 846 } 847 848 Set<Taggable> existing = tag.getTagged(); 849 850 if ( testConstraint( dm )){ 851 852 if ( auto_add ){ 853 854 if ( !existing.contains( dm )){ 855 856 if( canAddTaggable( dm )){ 857 858 tag.addTaggable( dm ); 859 } 860 } 861 } 862 }else{ 863 864 if ( auto_remove ){ 865 866 if ( existing.contains( dm )){ 867 868 tag.removeTaggable( dm ); 869 } 870 } 871 } 872 } 873 874 private void apply( List<DownloadManager> dms )875 apply( 876 List<DownloadManager> dms ) 877 { 878 if ( expr == null ){ 879 880 return; 881 } 882 883 Set<Taggable> existing = tag.getTagged(); 884 885 for ( DownloadManager dm: dms ){ 886 887 if ( dm.isDestroyed() || !dm.isPersistent()){ 888 889 continue; 890 } 891 892 if ( testConstraint( dm )){ 893 894 if ( auto_add ){ 895 896 if ( !existing.contains( dm )){ 897 898 if ( canAddTaggable( dm )){ 899 900 tag.addTaggable( dm ); 901 } 902 } 903 } 904 }else{ 905 906 if ( auto_remove ){ 907 908 if ( existing.contains( dm )){ 909 910 tag.removeTaggable( dm ); 911 } 912 } 913 } 914 } 915 } 916 917 918 919 private boolean canAddTaggable( DownloadManager dm )920 canAddTaggable( 921 DownloadManager dm ) 922 { 923 long now = SystemTime.getMonotonousTime(); 924 925 Map<DownloadManager,Long> recent_dms = handler.apply_history.get( tag ); 926 927 if ( recent_dms != null ){ 928 929 Long time = recent_dms.get( dm ); 930 931 if ( time != null && now - time < 1000 ){ 932 933 System.out.println( "Not applying constraint as too recently actioned: " + dm.getDisplayName() + "/" + tag.getTagName( true )); 934 935 return( false ); 936 } 937 } 938 939 if ( recent_dms == null ){ 940 941 recent_dms = new HashMap<DownloadManager,Long>(); 942 943 handler.apply_history.put( tag, recent_dms ); 944 } 945 946 recent_dms.put( dm, now ); 947 948 return( true ); 949 } 950 951 private boolean testConstraint( DownloadManager dm )952 testConstraint( 953 DownloadManager dm ) 954 { 955 List<Tag> dm_tags = handler.tag_manager.getTagsForTaggable( dm ); 956 957 return( expr.eval( dm, dm_tags )); 958 } 959 960 private interface 961 ConstraintExpr 962 { 963 public boolean eval( DownloadManager dm, List<Tag> tags )964 eval( 965 DownloadManager dm, 966 List<Tag> tags ); 967 968 public String getString()969 getString(); 970 } 971 972 private class 973 ConstraintExprTrue 974 implements ConstraintExpr 975 { 976 public boolean eval( DownloadManager dm, List<Tag> tags )977 eval( 978 DownloadManager dm, 979 List<Tag> tags ) 980 { 981 return( true ); 982 } 983 984 public String getString()985 getString() 986 { 987 return( "true" ); 988 } 989 } 990 991 private class 992 ConstraintExprParams 993 implements ConstraintExpr 994 { 995 private String value; 996 997 private ConstraintExprParams( String _value )998 ConstraintExprParams( 999 String _value ) 1000 { 1001 value = _value.trim(); 1002 } 1003 1004 public boolean eval( DownloadManager dm, List<Tag> tags )1005 eval( 1006 DownloadManager dm, 1007 List<Tag> tags ) 1008 { 1009 return( false ); 1010 } 1011 1012 public Object[] getValues()1013 getValues() 1014 { 1015 if ( value.length() == 0 ){ 1016 1017 return( new String[0]); 1018 1019 }else if ( !value.contains( "," )){ 1020 1021 return( new Object[]{ value }); 1022 1023 }else{ 1024 1025 char[] chars = value.toCharArray(); 1026 1027 boolean in_quote = false; 1028 1029 List<String> params = new ArrayList<String>(16); 1030 1031 StringBuffer current_param = new StringBuffer( value.length()); 1032 1033 for (int i=0;i<chars.length;i++){ 1034 1035 char c = chars[i]; 1036 1037 if ( c == '"' ){ 1038 1039 if ( i == 0 || chars[i-1] != '\\' ){ 1040 1041 in_quote = !in_quote; 1042 } 1043 } 1044 1045 if ( c == ',' && !in_quote ){ 1046 1047 params.add( current_param.toString()); 1048 1049 current_param.setLength( 0 ); 1050 1051 }else{ 1052 1053 if ( in_quote || !Character.isWhitespace( c )){ 1054 1055 current_param.append( c ); 1056 } 1057 } 1058 } 1059 1060 params.add( current_param.toString()); 1061 1062 return( params.toArray( new Object[ params.size()])); 1063 } 1064 } 1065 1066 public String getString()1067 getString() 1068 { 1069 return( value ); 1070 } 1071 } 1072 1073 private class 1074 ConstraintExprNot 1075 implements ConstraintExpr 1076 { 1077 private ConstraintExpr expr; 1078 1079 private ConstraintExprNot( ConstraintExpr e )1080 ConstraintExprNot( 1081 ConstraintExpr e ) 1082 { 1083 expr = e; 1084 } 1085 1086 public boolean eval( DownloadManager dm, List<Tag> tags )1087 eval( 1088 DownloadManager dm, 1089 List<Tag> tags ) 1090 { 1091 return( !expr.eval( dm, tags )); 1092 } 1093 1094 public String getString()1095 getString() 1096 { 1097 return( "!(" + expr.getString() + ")"); 1098 } 1099 } 1100 1101 private class 1102 ConstraintExprOr 1103 implements ConstraintExpr 1104 { 1105 private ConstraintExpr[] exprs; 1106 1107 private ConstraintExprOr( ConstraintExpr[] _exprs )1108 ConstraintExprOr( 1109 ConstraintExpr[] _exprs ) 1110 { 1111 exprs = _exprs; 1112 } 1113 1114 public boolean eval( DownloadManager dm, List<Tag> tags )1115 eval( 1116 DownloadManager dm, 1117 List<Tag> tags ) 1118 { 1119 for ( ConstraintExpr expr: exprs ){ 1120 1121 if ( expr.eval( dm, tags )){ 1122 1123 return( true ); 1124 } 1125 } 1126 1127 return( false ); 1128 } 1129 1130 public String getString()1131 getString() 1132 { 1133 String res = ""; 1134 1135 for ( int i=0;i<exprs.length;i++){ 1136 1137 res += (i==0?"":"||") + exprs[i].getString(); 1138 } 1139 1140 return( "(" + res + ")" ); 1141 } 1142 } 1143 1144 private class 1145 ConstraintExprAnd 1146 implements ConstraintExpr 1147 { 1148 private ConstraintExpr[] exprs; 1149 1150 private ConstraintExprAnd( ConstraintExpr[] _exprs )1151 ConstraintExprAnd( 1152 ConstraintExpr[] _exprs ) 1153 { 1154 exprs = _exprs; 1155 } 1156 1157 public boolean eval( DownloadManager dm, List<Tag> tags )1158 eval( 1159 DownloadManager dm, 1160 List<Tag> tags ) 1161 { 1162 for ( ConstraintExpr expr: exprs ){ 1163 1164 if ( !expr.eval( dm, tags )){ 1165 1166 return( false ); 1167 } 1168 } 1169 1170 return( true ); 1171 } 1172 1173 public String getString()1174 getString() 1175 { 1176 String res = ""; 1177 1178 for ( int i=0;i<exprs.length;i++){ 1179 1180 res += (i==0?"":"&&") + exprs[i].getString(); 1181 } 1182 1183 return( "(" + res + ")" ); 1184 } 1185 } 1186 1187 private class 1188 ConstraintExprXor 1189 implements ConstraintExpr 1190 { 1191 private ConstraintExpr[] exprs; 1192 1193 private ConstraintExprXor( ConstraintExpr[] _exprs )1194 ConstraintExprXor( 1195 ConstraintExpr[] _exprs ) 1196 { 1197 exprs = _exprs; 1198 1199 if ( exprs.length < 2 ){ 1200 1201 throw( new RuntimeException( "Two or more arguments required for ^" )); 1202 } 1203 } 1204 1205 public boolean eval( DownloadManager dm, List<Tag> tags )1206 eval( 1207 DownloadManager dm, 1208 List<Tag> tags ) 1209 { 1210 boolean res = exprs[0].eval( dm, tags ); 1211 1212 for ( int i=1;i<exprs.length;i++){ 1213 1214 res = res ^ exprs[i].eval( dm, tags ); 1215 } 1216 1217 return( res ); 1218 } 1219 1220 public String getString()1221 getString() 1222 { 1223 String res = ""; 1224 1225 for ( int i=0;i<exprs.length;i++){ 1226 1227 res += (i==0?"":"^") + exprs[i].getString(); 1228 } 1229 1230 return( "(" + res + ")" ); 1231 } 1232 } 1233 1234 private static final int FT_HAS_TAG = 1; 1235 private static final int FT_IS_PRIVATE = 2; 1236 1237 private static final int FT_GE = 3; 1238 private static final int FT_GT = 4; 1239 private static final int FT_LE = 5; 1240 private static final int FT_LT = 6; 1241 private static final int FT_EQ = 7; 1242 private static final int FT_NEQ = 8; 1243 1244 private static final int FT_CONTAINS = 9; 1245 private static final int FT_MATCHES = 10; 1246 1247 private static final int FT_HAS_NET = 11; 1248 private static final int FT_IS_COMPLETE = 12; 1249 private static final int FT_CAN_ARCHIVE = 13; 1250 private static final int FT_IS_FORCE_START = 14; 1251 private static final int FT_JAVASCRIPT = 15; 1252 private static final int FT_IS_CHECKING = 16; 1253 private static final int FT_IS_STOPPED = 17; 1254 private static final int FT_IS_PAUSED = 18; 1255 1256 1257 private static Map<String,Integer> keyword_map = new HashMap<String, Integer>(); 1258 1259 private static final int KW_SHARE_RATIO = 0; 1260 private static final int KW_AGE = 1; 1261 private static final int KW_PERCENT = 2; 1262 private static final int KW_DOWNLOADING_FOR = 3; 1263 private static final int KW_SEEDING_FOR = 4; 1264 private static final int KW_SWARM_MERGE = 5; 1265 private static final int KW_LAST_ACTIVE = 6; 1266 private static final int KW_SEED_COUNT = 7; 1267 private static final int KW_PEER_COUNT = 8; 1268 private static final int KW_SEED_PEER_RATIO = 9; 1269 private static final int KW_RESUME_IN = 10; 1270 1271 static{ 1272 keyword_map.put( "shareratio", KW_SHARE_RATIO ); 1273 keyword_map.put( "share_ratio", KW_SHARE_RATIO ); 1274 keyword_map.put( "age", KW_AGE ); 1275 keyword_map.put( "percent", KW_PERCENT ); 1276 keyword_map.put( "downloadingfor", KW_DOWNLOADING_FOR ); 1277 keyword_map.put( "downloading_for", KW_DOWNLOADING_FOR ); 1278 keyword_map.put( "seedingfor", KW_SEEDING_FOR ); 1279 keyword_map.put( "seeding_for", KW_SEEDING_FOR ); 1280 keyword_map.put( "swarmmergebytes", KW_SWARM_MERGE ); 1281 keyword_map.put( "swarm_merge_bytes", KW_SWARM_MERGE ); 1282 keyword_map.put( "lastactive", KW_LAST_ACTIVE ); 1283 keyword_map.put( "last_active", KW_LAST_ACTIVE ); 1284 keyword_map.put( "seedcount", KW_SEED_COUNT ); 1285 keyword_map.put( "seed_count", KW_SEED_COUNT ); 1286 keyword_map.put( "peercount", KW_PEER_COUNT ); 1287 keyword_map.put( "peer_count", KW_PEER_COUNT ); 1288 keyword_map.put( "seedpeerratio", KW_SEED_PEER_RATIO ); 1289 keyword_map.put( "seed_peer_ratio", KW_SEED_PEER_RATIO ); 1290 keyword_map.put( "resumein", KW_RESUME_IN ); 1291 keyword_map.put( "resume_in", KW_RESUME_IN ); 1292 } 1293 1294 private class 1295 ConstraintExprFunction 1296 implements ConstraintExpr 1297 { 1298 1299 private final String func_name; 1300 private final ConstraintExprParams params_expr; 1301 private final Object[] params; 1302 1303 private final int fn_type; 1304 1305 private ConstraintExprFunction( String _func_name, ConstraintExprParams _params )1306 ConstraintExprFunction( 1307 String _func_name, 1308 ConstraintExprParams _params ) 1309 { 1310 func_name = _func_name; 1311 params_expr = _params; 1312 1313 params = _params.getValues(); 1314 1315 boolean params_ok = false; 1316 1317 if ( func_name.equals( "hasTag" )){ 1318 1319 fn_type = FT_HAS_TAG; 1320 1321 params_ok = params.length == 1 && getStringLiteral( params, 0 ); 1322 1323 }else if ( func_name.equals( "hasNet" )){ 1324 1325 fn_type = FT_HAS_NET; 1326 1327 params_ok = params.length == 1 && getStringLiteral( params, 0 ); 1328 1329 if ( params_ok ){ 1330 1331 params[0] = AENetworkClassifier.internalise((String)params[0]); 1332 1333 params_ok = params[0] != null; 1334 } 1335 }else if ( func_name.equals( "isPrivate" )){ 1336 1337 fn_type = FT_IS_PRIVATE; 1338 1339 params_ok = params.length == 0; 1340 1341 }else if ( func_name.equals( "isForceStart" )){ 1342 1343 fn_type = FT_IS_FORCE_START; 1344 1345 depends_on_download_state = true; 1346 1347 params_ok = params.length == 0; 1348 1349 }else if ( func_name.equals( "isChecking" )){ 1350 1351 fn_type = FT_IS_CHECKING; 1352 1353 depends_on_download_state = true; 1354 1355 params_ok = params.length == 0; 1356 1357 }else if ( func_name.equals( "isComplete" )){ 1358 1359 fn_type = FT_IS_COMPLETE; 1360 1361 depends_on_download_state = true; 1362 1363 params_ok = params.length == 0; 1364 1365 }else if ( func_name.equals( "isStopped" )){ 1366 1367 fn_type = FT_IS_STOPPED; 1368 1369 depends_on_download_state = true; 1370 1371 params_ok = params.length == 0; 1372 1373 }else if ( func_name.equals( "isPaused" )){ 1374 1375 fn_type = FT_IS_PAUSED; 1376 1377 depends_on_download_state = true; 1378 1379 params_ok = params.length == 0; 1380 1381 }else if ( func_name.equals( "canArchive" )){ 1382 1383 fn_type = FT_CAN_ARCHIVE; 1384 1385 params_ok = params.length == 0; 1386 1387 }else if ( func_name.equals( "isGE" )){ 1388 1389 fn_type = FT_GE; 1390 1391 params_ok = params.length == 2; 1392 1393 }else if ( func_name.equals( "isGT" )){ 1394 1395 fn_type = FT_GT; 1396 1397 params_ok = params.length == 2; 1398 1399 }else if ( func_name.equals( "isLE" )){ 1400 1401 fn_type = FT_LE; 1402 1403 params_ok = params.length == 2; 1404 1405 }else if ( func_name.equals( "isLT" )){ 1406 1407 fn_type = FT_LT; 1408 1409 params_ok = params.length == 2; 1410 1411 }else if ( func_name.equals( "isEQ" )){ 1412 1413 fn_type = FT_EQ; 1414 1415 params_ok = params.length == 2; 1416 1417 }else if ( func_name.equals( "isNEQ" )){ 1418 1419 fn_type = FT_NEQ; 1420 1421 params_ok = params.length == 2; 1422 1423 }else if ( func_name.equals( "contains" )){ 1424 1425 fn_type = FT_CONTAINS; 1426 1427 params_ok = params.length == 2; 1428 1429 }else if ( func_name.equals( "matches" )){ 1430 1431 fn_type = FT_MATCHES; 1432 1433 params_ok = params.length == 2 && getStringLiteral( params, 1 ); 1434 1435 }else if ( func_name.equals( "javascript" )){ 1436 1437 fn_type = FT_JAVASCRIPT; 1438 1439 params_ok = params.length == 1 && getStringLiteral( params, 0 ); 1440 1441 depends_on_download_state = true; // dunno so let's assume so 1442 1443 }else{ 1444 1445 throw( new RuntimeException( "Unsupported function '" + func_name + "'" )); 1446 } 1447 1448 if ( !params_ok ){ 1449 1450 throw( new RuntimeException( "Invalid parameters for function '" + func_name + "': " + params_expr.getString())); 1451 1452 } 1453 } 1454 1455 public boolean eval( DownloadManager dm, List<Tag> tags )1456 eval( 1457 DownloadManager dm, 1458 List<Tag> tags ) 1459 { 1460 switch( fn_type ){ 1461 case FT_HAS_TAG:{ 1462 1463 String tag_name = (String)params[0]; 1464 1465 for ( Tag t: tags ){ 1466 1467 if ( t.getTagName( true ).equals( tag_name )){ 1468 1469 return( true ); 1470 } 1471 } 1472 1473 return( false ); 1474 } 1475 case FT_HAS_NET:{ 1476 1477 String net_name = (String)params[0]; 1478 1479 if ( net_name != null ){ 1480 1481 String[] nets = dm.getDownloadState().getNetworks(); 1482 1483 if ( nets != null ){ 1484 1485 for ( String net: nets ){ 1486 1487 if ( net == net_name ){ 1488 1489 return( true ); 1490 } 1491 } 1492 } 1493 } 1494 1495 return( false ); 1496 } 1497 case FT_IS_PRIVATE:{ 1498 1499 TOTorrent t = dm.getTorrent(); 1500 1501 return( t != null && t.getPrivate()); 1502 } 1503 case FT_IS_FORCE_START:{ 1504 1505 return( dm.isForceStart()); 1506 } 1507 case FT_IS_CHECKING:{ 1508 1509 int state = dm.getState(); 1510 1511 if ( state == DownloadManager.STATE_CHECKING ){ 1512 1513 return( true ); 1514 1515 }else if ( state == DownloadManager.STATE_SEEDING ){ 1516 1517 DiskManager disk_manager = dm.getDiskManager(); 1518 1519 if ( disk_manager != null ){ 1520 1521 return( disk_manager.getCompleteRecheckStatus() != -1 ); 1522 } 1523 } 1524 1525 return( false ); 1526 } 1527 case FT_IS_COMPLETE:{ 1528 1529 return( dm.isDownloadComplete( false )); 1530 } 1531 case FT_IS_STOPPED:{ 1532 1533 int state = dm.getState(); 1534 1535 return( state == DownloadManager.STATE_STOPPED && !dm.isPaused()); 1536 } 1537 case FT_IS_PAUSED:{ 1538 1539 return( dm.isPaused()); 1540 } 1541 case FT_CAN_ARCHIVE:{ 1542 1543 Download dl = PluginCoreUtils.wrap( dm ); 1544 1545 return( dl != null && dl.canStubbify()); 1546 } 1547 case FT_GE: 1548 case FT_GT: 1549 case FT_LE: 1550 case FT_LT: 1551 case FT_EQ: 1552 case FT_NEQ:{ 1553 1554 Number n1 = getNumeric( dm, params, 0 ); 1555 Number n2 = getNumeric( dm, params, 1 ); 1556 1557 switch( fn_type ){ 1558 1559 case FT_GE: 1560 return( n1.doubleValue() >= n2.doubleValue()); 1561 case FT_GT: 1562 return( n1.doubleValue() > n2.doubleValue()); 1563 case FT_LE: 1564 return( n1.doubleValue() <= n2.doubleValue()); 1565 case FT_LT: 1566 return( n1.doubleValue() < n2.doubleValue()); 1567 case FT_EQ: 1568 return( n1.doubleValue() == n2.doubleValue()); 1569 case FT_NEQ: 1570 return( n1.doubleValue() != n2.doubleValue()); 1571 } 1572 1573 return( false ); 1574 } 1575 case FT_CONTAINS:{ 1576 1577 String s1 = getString( dm, params, 0 ); 1578 String s2 = getString( dm, params, 1 ); 1579 1580 return( s1.contains( s2 )); 1581 } 1582 case FT_MATCHES:{ 1583 1584 String s1 = getString( dm, params, 0 ); 1585 1586 if ( params[1] == null ){ 1587 1588 return( false ); 1589 1590 }else if ( params[1] instanceof Pattern ){ 1591 1592 return(((Pattern)params[1]).matcher( s1 ).find()); 1593 1594 }else{ 1595 1596 try{ 1597 Pattern p = Pattern.compile((String)params[1], Pattern.CASE_INSENSITIVE ); 1598 1599 params[1] = p; 1600 1601 return( p.matcher( s1 ).find()); 1602 1603 }catch( Throwable e ){ 1604 1605 Debug.out( "Invalid constraint pattern: " + params[1] ); 1606 1607 params[1] = null; 1608 } 1609 } 1610 1611 return( false ); 1612 } 1613 case FT_JAVASCRIPT:{ 1614 1615 Object result = 1616 handler.tag_manager.evalScript( 1617 tag, 1618 "javascript( " + (String)params[0] + ")", 1619 dm, 1620 "inTag" ); 1621 1622 if ( result instanceof Boolean ){ 1623 1624 return((Boolean)result); 1625 } 1626 1627 return( false ); 1628 } 1629 } 1630 1631 return( false ); 1632 } 1633 1634 private boolean getStringLiteral( Object[] args, int index )1635 getStringLiteral( 1636 Object[] args, 1637 int index ) 1638 { 1639 Object _arg = args[index]; 1640 1641 if ( _arg instanceof String ){ 1642 1643 String arg = (String)_arg; 1644 1645 if ( arg.startsWith( "\"" ) && arg.endsWith( "\"" )){ 1646 1647 args[index] = arg.substring( 1, arg.length() - 1 ); 1648 1649 return( true ); 1650 } 1651 } 1652 1653 return( false ); 1654 } 1655 1656 private String getString( DownloadManager dm, Object[] args, int index )1657 getString( 1658 DownloadManager dm, 1659 Object[] args, 1660 int index ) 1661 { 1662 String str = (String)args[index]; 1663 1664 if ( str.startsWith( "\"" ) && str.endsWith( "\"" )){ 1665 1666 return( str.substring( 1, str.length() - 1 )); 1667 1668 }else if ( str.equals( "name" )){ 1669 1670 return( dm.getDisplayName()); 1671 1672 }else{ 1673 1674 Debug.out( "Invalid constraint string: " + str ); 1675 1676 String result = "\"\""; 1677 1678 args[index] = result; 1679 1680 return( result ); 1681 } 1682 } 1683 1684 private Number getNumeric( DownloadManager dm, Object[] args, int index )1685 getNumeric( 1686 DownloadManager dm, 1687 Object[] args, 1688 int index ) 1689 { 1690 Object arg = args[index]; 1691 1692 if ( arg instanceof Number ){ 1693 1694 return((Number)arg); 1695 } 1696 1697 String str = (String)arg; 1698 1699 Number result = 0; 1700 1701 try{ 1702 if ( Character.isDigit( str.charAt(0))){ 1703 1704 if ( str.contains( "." )){ 1705 1706 result = Float.parseFloat( str ); 1707 1708 }else{ 1709 1710 result = Long.parseLong( str ); 1711 } 1712 1713 return( result ); 1714 }else{ 1715 1716 Integer kw = keyword_map.get( str.toLowerCase( Locale.US )); 1717 1718 if ( kw == null ){ 1719 1720 Debug.out( "Invalid constraint keyword: " + str ); 1721 1722 return( result ); 1723 } 1724 1725 switch( kw ){ 1726 case KW_SHARE_RATIO:{ 1727 result = null; // don't cache this! 1728 1729 int sr = dm.getStats().getShareRatio(); 1730 1731 if ( sr == -1 ){ 1732 1733 return( Integer.MAX_VALUE ); 1734 1735 }else{ 1736 1737 return( new Float( sr/1000.0f )); 1738 } 1739 } 1740 case KW_PERCENT:{ 1741 1742 result = null; // don't cache this! 1743 1744 // 0->1000 1745 1746 int percent = dm.getStats().getPercentDoneExcludingDND(); 1747 1748 return( new Float( percent/10.0f )); 1749 } 1750 case KW_AGE:{ 1751 1752 result = null; // don't cache this! 1753 1754 long added = dm.getDownloadState().getLongParameter( DownloadManagerState.PARAM_DOWNLOAD_ADDED_TIME ); 1755 1756 if ( added <= 0 ){ 1757 1758 return( 0 ); 1759 } 1760 1761 return(( SystemTime.getCurrentTime() - added )/1000 ); // secs 1762 } 1763 case KW_DOWNLOADING_FOR:{ 1764 1765 result = null; // don't cache this! 1766 1767 return( dm.getStats().getSecondsDownloading()); 1768 } 1769 case KW_SEEDING_FOR:{ 1770 1771 result = null; // don't cache this! 1772 1773 return( dm.getStats().getSecondsOnlySeeding()); 1774 } 1775 case KW_LAST_ACTIVE:{ 1776 1777 result = null; // don't cache this! 1778 1779 DownloadManagerState dms = dm.getDownloadState(); 1780 1781 long timestamp = dms.getLongAttribute( DownloadManagerState.AT_LAST_ADDED_TO_ACTIVE_TAG ); 1782 1783 if ( timestamp <= 0 ){ 1784 1785 return( Long.MAX_VALUE ); 1786 } 1787 1788 return(( SystemTime.getCurrentTime() - timestamp )/1000 ); 1789 } 1790 case KW_RESUME_IN:{ 1791 1792 result = null; // don't cache this! 1793 1794 long resume_millis = dm.getAutoResumeTime(); 1795 1796 long now = SystemTime.getCurrentTime(); 1797 1798 if ( resume_millis <= 0 || resume_millis <= now ){ 1799 1800 return( 0 ); 1801 } 1802 1803 return(( resume_millis - now )/1000 ); 1804 } 1805 1806 case KW_SWARM_MERGE:{ 1807 1808 result = null; // don't cache this! 1809 1810 return( dm.getDownloadState().getLongAttribute( DownloadManagerState.AT_MERGED_DATA )); 1811 } 1812 case KW_SEED_COUNT:{ 1813 1814 result = null; // don't cache this! 1815 1816 TRTrackerScraperResponse response = dm.getTrackerScrapeResponse(); 1817 1818 int seeds = dm.getNbSeeds(); 1819 1820 if ( response != null && response.isValid()){ 1821 1822 seeds = Math.max( seeds, response.getSeeds()); 1823 } 1824 1825 return( Math.max( 0, seeds )); 1826 } 1827 case KW_PEER_COUNT:{ 1828 1829 result = null; // don't cache this! 1830 1831 TRTrackerScraperResponse response = dm.getTrackerScrapeResponse(); 1832 1833 int peers = dm.getNbSeeds(); 1834 1835 if ( response != null && response.isValid()){ 1836 1837 peers = Math.max( peers, response.getPeers()); 1838 } 1839 1840 return( Math.max( 0, peers )); 1841 } 1842 case KW_SEED_PEER_RATIO:{ 1843 1844 result = null; // don't cache this! 1845 1846 TRTrackerScraperResponse response = dm.getTrackerScrapeResponse(); 1847 1848 int seeds = dm.getNbSeeds(); 1849 int peers = dm.getNbSeeds(); 1850 1851 if ( response != null && response.isValid()){ 1852 1853 seeds = Math.max( seeds, response.getSeeds()); 1854 peers = Math.max( peers, response.getPeers()); 1855 } 1856 1857 float ratio; 1858 1859 if ( peers < 0 || seeds < 0 ){ 1860 1861 ratio = 0; 1862 1863 }else{ 1864 1865 if ( peers == 0 ){ 1866 1867 if ( seeds == 0 ){ 1868 1869 ratio = 0; 1870 1871 }else{ 1872 1873 ratio = Float.MAX_VALUE; 1874 } 1875 }else{ 1876 1877 ratio = (float)seeds/peers; 1878 } 1879 } 1880 1881 return( ratio ); 1882 } 1883 default:{ 1884 1885 Debug.out( "Invalid constraint keyword: " + str ); 1886 1887 return( result ); 1888 } 1889 } 1890 } 1891 }catch( Throwable e){ 1892 1893 Debug.out( "Invalid constraint numeric: " + str ); 1894 1895 return( result ); 1896 1897 }finally{ 1898 1899 if ( result != null ){ 1900 1901 // cache literal results 1902 1903 args[index] = result; 1904 } 1905 } 1906 } 1907 1908 public String getString()1909 getString() 1910 { 1911 return( func_name + "(" + params_expr.getString() + ")" ); 1912 } 1913 } 1914 } 1915 1916 public static void main( String[] args )1917 main( 1918 String[] args ) 1919 { 1920 TagPropertyConstraintHandler handler = new TagPropertyConstraintHandler(); 1921 1922 //System.out.println( handler.compileConstraint( "!(hasTag(\"bil\") && (hasTag( \"fred\" ))) || hasTag(\"toot\")" ).getString()); 1923 System.out.println( handler.compileConstraint( "isGE( shareratio, 1.5)" ).getString()); 1924 } 1925 } 1926