2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview 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 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.bin.argparser;
24 import java.io.IOException;
25 import java.nio.file.Files;
26 import java.nio.file.Paths;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.EnumSet;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Locale;
35 import jalview.bin.Cache;
36 import jalview.bin.Console;
37 import jalview.bin.Jalview;
38 import jalview.bin.argparser.Arg.Opt;
39 import jalview.util.FileUtils;
40 import jalview.util.HttpUtils;
42 public class ArgParser
44 protected static final String DOUBLEDASH = "--";
46 protected static final char EQUALS = '=';
48 protected static final String NEGATESTRING = "no";
50 // the default linked id prefix used for no id (not even square braces)
51 protected static final String DEFAULTLINKEDIDPREFIX = "JALVIEW:";
53 // the linkedId string used to match all linkedIds seen so far
54 protected static final String MATCHALLLINKEDIDS = "*";
56 // the counter added to the default linked id prefix
57 private int defaultLinkedIdCounter = 0;
59 // the substitution string used to use the defaultLinkedIdCounter
60 private static final String DEFAULTLINKEDIDCOUNTER = "{}";
62 // the counter added to the default linked id prefix. NOW using
63 // linkedIdAutoCounter
64 // private int openLinkedIdCounter = 0;
66 // the linked id prefix used for --open files. NOW the same as DEFAULT
67 protected static final String OPENLINKEDIDPREFIX = DEFAULTLINKEDIDPREFIX;
69 // the counter used for {n} substitutions
70 private int linkedIdAutoCounter = 0;
72 // the linked id substitution string used to increment the idCounter (and use
73 // the incremented value)
74 private static final String INCREMENTLINKEDIDAUTOCOUNTER = "{++n}";
76 // the linked id substitution string used to use the idCounter
77 private static final String LINKEDIDAUTOCOUNTER = "{n}";
79 // the linked id substitution string used to use the base filename of --append
81 private static final String LINKEDIDBASENAME = "{basename}";
83 // the linked id substitution string used to use the dir path of --append
85 private static final String LINKEDIDDIRNAME = "{dirname}";
87 // the current argfile
88 private String argFile = null;
90 // the linked id substitution string used to use the dir path of the latest
92 private static final String ARGFILEBASENAME = "{argfilebasename}";
94 // the linked id substitution string used to use the dir path of the latest
96 private static final String ARGFILEDIRNAME = "{argfiledirname}";
98 // flag to say whether {n} subtitutions in output filenames should be made.
99 // Turn on and off with --subs and --nosubs
100 private boolean substitutions = false;
102 // flag to say whether the default linkedId is the current default linked id
104 private boolean allLinkedIds = false;
106 protected static final Map<String, Arg> argMap;
108 protected Map<String, ArgValuesMap> linkedArgs = new HashMap<>();
110 protected List<String> linkedOrder = null;
112 protected List<Arg> argList;
114 private static final char ARGFILECOMMENT = '#';
118 argMap = new HashMap<>();
119 for (Arg a : EnumSet.allOf(Arg.class))
121 for (String argName : a.getNames())
123 if (argMap.containsKey(argName))
125 Console.warn("Trying to add argument name multiple times: '"
126 + argName + "'"); // RESTORE THIS WHEN
128 if (argMap.get(argName) != a)
131 "Trying to add argument name multiple times for different Args: '"
132 + argMap.get(argName).getName() + ":" + argName
133 + "' and '" + a.getName() + ":" + argName
138 argMap.put(argName, a);
143 public ArgParser(String[] args)
148 public ArgParser(String[] args, boolean initsubstitutions)
150 // Make a mutable new ArrayList so that shell globbing parser works.
151 // (When shell file globbing is used, there are a sequence of non-Arg
152 // arguments (which are the expanded globbed filenames) that need to be
153 // consumed by the --append/--argfile/etc Arg which is most easily done by
154 // removing these filenames from the list one at a time. This can't be done
155 // with an ArrayList made with only Arrays.asList(String[] args). )
156 this(new ArrayList<>(Arrays.asList(args)), initsubstitutions);
159 public ArgParser(List<String> args, boolean initsubstitutions)
161 this(args, initsubstitutions, false);
164 public ArgParser(List<String> args, boolean initsubstitutions,
165 boolean allowPrivate)
167 // do nothing if there are no "--" args and (some "-" args || >0 arg is
171 for (String arg : args)
173 if (arg.startsWith(DOUBLEDASH))
178 else if (arg.startsWith("-") || arg.equals("open"))
185 // leave it to the old style -- parse an empty list
186 parse(new ArrayList<String>(), false, false);
189 parse(args, initsubstitutions, allowPrivate);
192 private void parse(List<String> args, boolean initsubstitutions,
193 boolean allowPrivate)
195 this.substitutions = initsubstitutions;
197 boolean openEachInitialFilenames = true;
198 for (int i = 0; i < args.size(); i++)
200 String arg = args.get(i);
202 // If the first arguments do not start with "--" or "-" or is "open" and
203 // is a filename that exists it is probably a file/list of files to open
204 // so we fake an Arg.OPEN argument and when adding files only add the
205 // single arg[i] and increment the defaultLinkedIdCounter so that each of
206 // these files is opened separately.
207 if (openEachInitialFilenames && !arg.startsWith(DOUBLEDASH)
208 && !arg.startsWith("-") && (new File(arg).exists()
209 || HttpUtils.startsWithHttpOrHttps(arg)))
211 arg = Arg.OPEN.argString();
215 openEachInitialFilenames = false;
218 String argName = null;
220 List<String> globVals = null; // for Opt.GLOB only
221 SubVals globSubVals = null; // also for use by Opt.GLOB only
222 String linkedId = null;
223 if (arg.startsWith(DOUBLEDASH))
225 int equalPos = arg.indexOf(EQUALS);
228 argName = arg.substring(DOUBLEDASH.length(), equalPos);
229 val = arg.substring(equalPos + 1);
233 argName = arg.substring(DOUBLEDASH.length());
235 int idOpen = argName.indexOf('[');
236 int idClose = argName.indexOf(']');
238 if (idOpen > -1 && idClose == argName.length() - 1)
240 linkedId = argName.substring(idOpen + 1, idClose);
241 argName = argName.substring(0, idOpen);
244 Arg a = argMap.get(argName);
245 // check for boolean prepended by "no"
246 boolean negated = false;
247 if (a == null && argName.startsWith(NEGATESTRING) && argMap
248 .containsKey(argName.substring(NEGATESTRING.length())))
250 argName = argName.substring(NEGATESTRING.length());
251 a = argMap.get(argName);
255 // check for config errors
259 Console.error("Argument '" + arg + "' not recognised. Ignoring.");
262 if (a.hasOption(Opt.PRIVATE) && !allowPrivate)
265 "Argument '" + a.argString() + "' is private. Ignoring.");
268 if (!a.hasOption(Opt.BOOLEAN) && negated)
270 // used "no" with a non-boolean option
271 Console.error("Argument '" + DOUBLEDASH + NEGATESTRING + argName
272 + "' not a boolean option. Ignoring.");
275 if (!a.hasOption(Opt.STRING) && equalPos > -1)
277 // set --argname=value when arg does not accept values
278 Console.error("Argument '" + a.argString()
279 + "' does not expect a value (given as '" + arg
283 if (!a.hasOption(Opt.LINKED) && linkedId != null)
285 // set --argname[linkedId] when arg does not use linkedIds
286 Console.error("Argument '" + a.argString()
287 + "' does not expect a linked id (given as '" + arg
293 if (a.hasOption(Opt.STRING))
297 if (a.hasOption(Opt.GLOB))
299 // strip off and save the SubVals to be added individually later
300 globSubVals = new SubVals(val);
301 // make substitutions before looking for files
302 String fileGlob = makeSubstitutions(globSubVals.getContent(),
304 globVals = FileUtils.getFilenamesFromGlob(fileGlob);
308 // val is already set -- will be saved in the ArgValue later in
314 // There is no "=" so value is next arg or args (possibly shell
316 if ((openEachInitialFilenames ? i : i + 1) >= args.size())
318 // no value to take for arg, which wants a value
319 Console.error("Argument '" + a.getName()
320 + "' requires a value, none given. Ignoring.");
323 // deal with bash globs here (--arg val* is expanded before reaching
324 // the JVM). Note that SubVals cannot be used in this case.
325 // If using the --arg=val then the glob is preserved and Java globs
326 // will be used later. SubVals can be used.
327 if (a.hasOption(Opt.GLOB))
329 // if this is the first argument with a file list at the start of
330 // the args we add filenames from index i instead of i+1
331 globVals = getShellGlobbedFilenameValues(a, args,
332 openEachInitialFilenames ? i : i + 1);
336 val = args.get(i + 1);
341 // make NOACTION adjustments
342 // default and auto counter increments
343 if (a == Arg.NEWFRAME)
345 defaultLinkedIdCounter++;
347 else if (a == Arg.NPP)
349 linkedIdAutoCounter++;
351 else if (a == Arg.SUBSTITUTIONS)
353 substitutions = !negated;
355 else if (a == Arg.SETARGFILE)
359 else if (a == Arg.UNSETARGFILE)
363 else if (a == Arg.ALLFRAMES)
365 allLinkedIds = !negated;
368 String autoCounterString = null;
369 boolean usingAutoCounterLinkedId = false;
370 String defaultLinkedId = new StringBuilder(DEFAULTLINKEDIDPREFIX)
371 .append(Integer.toString(defaultLinkedIdCounter))
373 boolean usingDefaultLinkedId = false;
374 if (a.hasOption(Opt.LINKED))
376 if (linkedId == null)
380 // use the next default prefixed OPENLINKEDID
381 // NOW using the linkedIdAutoCounter
382 defaultLinkedIdCounter++;
383 linkedId = new StringBuilder(OPENLINKEDIDPREFIX)
384 .append(Integer.toString(defaultLinkedIdCounter))
389 if (allLinkedIds && a.hasOption(Opt.ALLOWALL))
391 linkedId = this.MATCHALLLINKEDIDS;
395 // use default linkedId for linked arguments
396 linkedId = defaultLinkedId;
397 usingDefaultLinkedId = true;
398 Console.debug("Changing linkedId to '" + linkedId
403 else if (linkedId.contains(LINKEDIDAUTOCOUNTER))
405 // turn {n} to the autoCounter
406 autoCounterString = Integer.toString(linkedIdAutoCounter);
407 linkedId = linkedId.replace(LINKEDIDAUTOCOUNTER,
409 usingAutoCounterLinkedId = true;
411 "Changing linkedId to '" + linkedId + "' from " + arg);
413 else if (linkedId.contains(INCREMENTLINKEDIDAUTOCOUNTER))
415 // turn {++n} to the incremented autoCounter
416 autoCounterString = Integer.toString(++linkedIdAutoCounter);
417 linkedId = linkedId.replace(INCREMENTLINKEDIDAUTOCOUNTER,
419 usingAutoCounterLinkedId = true;
421 "Changing linkedId to '" + linkedId + "' from " + arg);
425 if (!linkedArgs.containsKey(linkedId))
426 linkedArgs.put(linkedId, new ArgValuesMap());
428 // do not continue for NOACTION args
429 if (a.hasOption(Opt.NOACTION))
432 ArgValuesMap avm = linkedArgs.get(linkedId);
434 // not dealing with both NODUPLICATEVALUES and GLOB
435 if (a.hasOption(Opt.NODUPLICATEVALUES) && avm.hasValue(a, val))
437 Console.error("Argument '" + a.argString()
438 + "' cannot contain a duplicate value ('" + val
439 + "'). Ignoring this and subsequent occurrences.");
443 // check for unique id
444 SubVals idsv = new SubVals(val);
445 String id = idsv.get(ArgValues.ID);
446 if (id != null && avm.hasId(a, id))
448 Console.error("Argument '" + a.argString()
449 + "' has a duplicate id ('" + id + "'). Ignoring.");
453 boolean argIndexIncremented = false;
455 * Change all avs.addValue() avs.setBoolean avs.setNegated() avs.incrementCount calls to checkfor linkedId == "*"
456 * DONE, need to check
458 ArgValues avs = avm.getOrCreateArgValues(a);
460 // store appropriate String value(s)
461 if (a.hasOption(Opt.STRING))
463 if (a.hasOption(Opt.GLOB) && globVals != null
464 && globVals.size() > 0)
466 for (String v : globVals)
468 SubVals vsv = new SubVals(globSubVals, v);
469 addValue(linkedId, avs, vsv, v, argIndex++, true);
470 argIndexIncremented = true;
475 addValue(linkedId, avs, val, argIndex, true);
478 else if (a.hasOption(Opt.BOOLEAN))
480 setBoolean(linkedId, avs, !negated, argIndex);
481 setNegated(linkedId, avs, negated);
483 else if (a.hasOption(Opt.UNARY))
485 setBoolean(linkedId, avs, true, argIndex);
487 incrementCount(linkedId, avs);
488 if (!argIndexIncremented)
491 // store in appropriate place
492 if (a.hasOption(Opt.LINKED))
494 // store the order of linkedIds
495 if (linkedOrder == null)
496 linkedOrder = new ArrayList<>();
497 if (!linkedOrder.contains(linkedId))
498 linkedOrder.add(linkedId);
501 // store arg in the list of args used
503 argList = new ArrayList<>();
504 if (!argList.contains(a))
511 public String makeSubstitutions(String val, String linkedId)
513 if (!this.substitutions || val == null)
518 if (val.indexOf('[') == 0 && val.indexOf(']') > 1)
520 int closeBracket = val.indexOf(']');
521 if (val.length() == closeBracket)
523 subvals = val.substring(0, closeBracket + 1);
524 rest = val.substring(closeBracket + 1);
531 if (rest.contains(LINKEDIDAUTOCOUNTER))
532 rest = rest.replace(LINKEDIDAUTOCOUNTER,
533 String.valueOf(linkedIdAutoCounter));
534 if (rest.contains(INCREMENTLINKEDIDAUTOCOUNTER))
535 rest = rest.replace(INCREMENTLINKEDIDAUTOCOUNTER,
536 String.valueOf(++linkedIdAutoCounter));
537 if (rest.contains(DEFAULTLINKEDIDCOUNTER))
538 rest = rest.replace(DEFAULTLINKEDIDCOUNTER,
539 String.valueOf(defaultLinkedIdCounter));
540 ArgValuesMap avm = linkedArgs.get(linkedId);
543 if (rest.contains(LINKEDIDBASENAME))
545 rest = rest.replace(LINKEDIDBASENAME, avm.getBasename());
547 if (rest.contains(LINKEDIDDIRNAME))
549 rest = rest.replace(LINKEDIDDIRNAME, avm.getDirname());
554 if (rest.contains(ARGFILEBASENAME))
556 rest = rest.replace(ARGFILEBASENAME,
557 FileUtils.getBasename(new File(argFile)));
559 if (rest.contains(ARGFILEDIRNAME))
561 rest = rest.replace(ARGFILEDIRNAME,
562 FileUtils.getDirname(new File(argFile)));
566 return new StringBuilder(subvals).append(rest).toString();
570 * A helper method to take a list of String args where we're expecting
571 * {"--previousargs", "--arg", "file1", "file2", "file3", "--otheroptionsornot"}
572 * and the index of the globbed arg, here 1. It returns a List<String> {"file1",
573 * "file2", "file3"} *and remove these from the original list object* so that
574 * processing can continue from where it has left off, e.g. args has become
575 * {"--previousargs", "--arg", "--otheroptionsornot"} so the next increment
576 * carries on from the next --arg if available.
578 protected static List<String> getShellGlobbedFilenameValues(Arg a,
579 List<String> args, int i)
581 List<String> vals = new ArrayList<>();
582 while (i < args.size() && !args.get(i).startsWith(DOUBLEDASH))
584 vals.add(FileUtils.substituteHomeDir(args.remove(i)));
585 if (!a.hasOption(Opt.GLOB))
591 public boolean isSet(Arg a)
593 return a.hasOption(Opt.LINKED) ? isSet("", a) : isSet(null, a);
596 public boolean isSet(String linkedId, Arg a)
598 ArgValuesMap avm = linkedArgs.get(linkedId);
599 return avm == null ? false : avm.containsArg(a);
602 public boolean getBool(Arg a)
604 if (!a.hasOption(Opt.BOOLEAN) && !a.hasOption(Opt.UNARY))
606 Console.warn("Getting boolean from non boolean Arg '" + a.getName()
609 return a.hasOption(Opt.LINKED) ? getBool("", a) : getBool(null, a);
612 public boolean getBool(String linkedId, Arg a)
614 ArgValuesMap avm = linkedArgs.get(linkedId);
616 return a.getDefaultBoolValue();
617 ArgValues avs = avm.getArgValues(a);
618 return avs == null ? a.getDefaultBoolValue() : avs.getBoolean();
621 public List<String> linkedIds()
626 public ArgValuesMap linkedArgs(String id)
628 return linkedArgs.get(id);
632 public String toString()
634 StringBuilder sb = new StringBuilder();
635 sb.append("UNLINKED\n");
636 sb.append(argValuesMapToString(linkedArgs.get(null)));
637 if (linkedIds() != null)
639 sb.append("LINKED\n");
640 for (String id : linkedIds())
642 // already listed these as UNLINKED args
646 ArgValuesMap avm = linkedArgs(id);
647 sb.append("ID: '").append(id).append("'\n");
648 sb.append(argValuesMapToString(avm));
651 return sb.toString();
654 private static String argValuesMapToString(ArgValuesMap avm)
658 StringBuilder sb = new StringBuilder();
659 for (Arg a : avm.getArgKeys())
661 ArgValues v = avm.getArgValues(a);
662 sb.append(v.toString());
665 return sb.toString();
668 public static ArgParser parseArgFiles(List<String> argFilenameGlobs,
669 boolean initsubstitutions)
671 List<File> argFiles = new ArrayList<>();
673 for (String pattern : argFilenameGlobs)
675 // I don't think we want to dedup files, making life easier
676 argFiles.addAll(FileUtils.getFilesFromGlob(pattern));
679 return parseArgFileList(argFiles, initsubstitutions);
682 public static ArgParser parseArgFileList(List<File> argFiles,
683 boolean initsubstitutions)
685 List<String> argsList = new ArrayList<>();
686 for (File argFile : argFiles)
688 if (!argFile.exists())
690 String message = Arg.ARGFILE.argString() + EQUALS + "\""
691 + argFile.getPath() + "\": File does not exist.";
692 Jalview.exit(message, 2);
696 String setargfile = new StringBuilder(Arg.SETARGFILE.argString())
697 .append(EQUALS).append(argFile.getCanonicalPath())
699 argsList.add(setargfile);
700 argsList.addAll(readArgFile(argFile));
701 argsList.add(Arg.UNSETARGFILE.argString());
702 } catch (IOException e)
704 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
705 + "\": File could not be read.";
706 Jalview.exit(message, 3);
709 // Third param "true" uses Opt.PRIVATE args --setargile=argfile and
711 return new ArgParser(argsList, initsubstitutions, true);
714 protected static List<String> readArgFile(File argFile)
716 List<String> args = new ArrayList<>();
717 if (argFile != null && argFile.exists())
721 for (String line : Files.readAllLines(Paths.get(argFile.getPath())))
723 if (line != null && line.length() > 0
724 && line.charAt(0) != ARGFILECOMMENT)
727 } catch (IOException e)
729 String message = Arg.ARGFILE.argString() + "=\"" + argFile.getPath()
730 + "\": File could not be read.";
731 Console.debug(message, e);
732 Jalview.exit(message, 3);
738 public static enum Position
743 public static String getValueFromSubValOrArg(ArgValuesMap avm, Arg a,
746 return getFromSubValArgOrPref(avm, a, sv, null, null, null);
749 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
750 SubVals sv, String key, String pref, String def)
752 return getFromSubValArgOrPref(avm, a, Position.FIRST, null, sv, key,
756 public static String getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
757 Position pos, ArgValue av, SubVals sv, String key, String pref,
762 if (sv != null && sv.has(key) && sv.get(key) != null)
764 if (avm != null && avm.containsArg(a))
767 if (pos == Position.FIRST && avm.getValue(a) != null)
768 return avm.getValue(a);
769 else if (pos == Position.BEFORE
770 && avm.getClosestPreviousArgValueOfArg(av, a) != null)
771 return avm.getClosestPreviousArgValueOfArg(av, a).getValue();
772 else if (pos == Position.AFTER
773 && avm.getClosestNextArgValueOfArg(av, a) != null)
774 return avm.getClosestNextArgValueOfArg(av, a).getValue();
776 return pref != null ? Cache.getDefault(pref, def) : def;
779 public static boolean getBoolFromSubValOrArg(ArgValuesMap avm, Arg a,
782 return getFromSubValArgOrPref(avm, a, sv, null, null, false);
785 public static boolean getFromSubValArgOrPref(ArgValuesMap avm, Arg a,
786 SubVals sv, String key, String pref, boolean def)
790 if (sv != null && sv.has(key) && sv.get(key) != null)
791 return sv.get(key).toLowerCase(Locale.ROOT).equals("true");
792 if (avm != null && avm.containsArg(a))
793 return avm.getBoolean(a);
794 return pref != null ? Cache.getDefault(pref, def) : def;
797 // the following methods look for the "*" linkedId and add the argvalue to all
798 // linkedId ArgValues if it does
799 private void addValue(String linkedId, ArgValues avs, SubVals sv,
800 String v, int argIndex, boolean doSubs)
803 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
805 for (String id : linkedOrder)
807 ArgValuesMap avm = linkedArgs.get(id);
808 ArgValues tavs = avm.getArgValues(a);
812 val = makeSubstitutions(v, linkedId);
813 sv = new SubVals(sv, val);
815 tavs.addValue(sv, val, argIndex);
823 val = makeSubstitutions(v, linkedId);
824 sv = new SubVals(sv, val);
826 avs.addValue(sv, val, argIndex);
830 private void addValue(String linkedId, ArgValues avs, String v,
831 int argIndex, boolean doSubs)
834 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
836 for (String id : linkedOrder)
838 ArgValuesMap avm = linkedArgs.get(id);
839 ArgValues tavs = avm.getArgValues(a);
840 String val = doSubs ? makeSubstitutions(v, linkedId) : v;
841 tavs.addValue(val, argIndex);
846 String val = doSubs ? makeSubstitutions(v, linkedId) : v;
847 avs.addValue(val, argIndex);
851 private void setBoolean(String linkedId, ArgValues avs, boolean b,
855 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
857 for (String id : linkedOrder)
859 ArgValuesMap avm = linkedArgs.get(id);
860 ArgValues tavs = avm.getArgValues(a);
861 tavs.setBoolean(b, argIndex);
866 avs.setBoolean(b, argIndex);
870 private void setNegated(String linkedId, ArgValues avs, boolean b)
873 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
875 for (String id : linkedOrder)
877 ArgValuesMap avm = linkedArgs.get(id);
878 ArgValues tavs = avm.getArgValues(a);
888 private void incrementCount(String linkedId, ArgValues avs)
891 if (MATCHALLLINKEDIDS.equals(linkedId) && a.hasOption(Opt.ALLOWALL))
893 for (String id : linkedOrder)
895 ArgValuesMap avm = linkedArgs.get(id);
896 ArgValues tavs = avm.getArgValues(a);
897 tavs.incrementCount();
902 avs.incrementCount();