code: private ActivityImplementor getImpl(File file) throws IOException {
          String contents = new String(Files.readAllBytes(file.toPath()));
  
          Matcher matcher = ACTIVITY_ANNOTATION.matcher(contents);
          if (matcher.find()) {
              // only implClass and image are needed (other values are hardcoded)
              String asset = file.getAbsolutePath().substring(assetLoc.getAbsolutePath().length() + 1).replace('/', '.');
              String implClass = asset.substring(0, asset.lastIndexOf('.'));
              String label = implClass.substring(implClass.lastIndexOf('.') + 1);
              String category = "com.centurylink.mdw.activity.types.GeneralActivity";
              String icon = "shape:activity";
              // set icon if specified
              String params = matcher.group(1);
              Matcher iconMatcher = ICON_VALUE.matcher(params);
              if (iconMatcher.find()) {
                  icon = iconMatcher.group(1);
              }
              return new ActivityImplementor(implClass, category, label, icon, "{}");
          }
          return null;
      }
docs: For annotation-based implementors.  Custom impl classes cannot be compiled, so this crude
  parsing mechanism is used to determine image icon.  Kotlin limitation: file name must be the same as impl class name.