static final String APPLICATION_JSON_CHARSET_UTF8 = "application/json; charset=utf-8"; static final String BEARER = "Bearer "; static final String CLOUD_RESOURCE_MANAGER_HOST = "cloudresourcemanager.googleapis.com"; static final String HEALTHCARE_HOST = "healthcare.googleapis.com"; public static final String INSTANCES = "/instances/"; static final String MULTIPART_RELATED_TYPE_IMAGE_JPEG_TRANSFER_SYNTAX =
    "multipart/related; type=image/jpeg; transfer-syntax=1.2.840.10008.1.2.4.50"; static final String PARAM_DATASET_ID = "datasetId"; static final String PARAM_DICOM_STORE_ID = "dicomStoreId"; static final String PARAM_INCLUDE_FIELD = "includefield"; static final String PARAM_PAGE_TOKEN = "pageToken"; static final String PATH_TO_PROJECTS = "/v1/projects/"; static final String SCHEME = "https"; public static final String SERIES = "/series/"; static final String VALUE_PARAM_CONCATENATION_FRAME_OFFSET_NUMBER = "00209228"; static final String VALUE_PARAM_CONCATENATION_UID = "00209161"; static final String VALUE_PARAM_DIMENSION_ORGANIZATION_TYPE = "00209311"; static final String VALUE_PARAM_IMAGE_COMMENTS = "00204000"; static final String VALUE_PARAM_INSTANCE_CREATION_DATE = "00080012"; static final String VALUE_PARAM_INSTANCE_CREATION_TIME = "00080013"; static final String VALUE_PARAM_MODALITY = "00080060"; static final String VALUE_PARAM_NUMBER_OF_FRAMES = "00209228"; static final String VALUE_PARAM_PER_FRAME_FUNCTIONAL_GROUP_SEQUENCE = "52009230"; static final String VALUE_PARAM_SERIES_INSTANCE_UID = "0020000E"; static final String VALUE_PARAM_SOP_AUTHORIZATHION_COMMENT = "01000424"; static final String VALUE_PARAM_STUDY_INSTANCE_UID = "0020000D"; static final String VALUE_PARAM_TIMEZONE_OFFSET_FROM_UTC = "00080201"; static final String VALUE_PARAM_TOTAL_PIXEL_MATRIX_COLUMNS = "00480006"; static final String VALUE_PARAM_TOTAL_PIXEL_MATRIX_ROWS = "00480007"; public static final String FAILED_HTTP = "Failed HTTP! Status code: "; public class DatasetsPathBuilder implements PathBuilder {

  private QueryBuilder queryBuilder;

  /**
   * Instantiates a new Datasets path builder.
   *
   * @param queryBuilder the query builder with parameters
   */
  public DatasetsPathBuilder(QueryBuilder queryBuilder) {
    this.queryBuilder = queryBuilder;
  }

  @Override
  public String toPath() throws QuPathCloudException {
    if (queryBuilder.getLocationId() == null) {
      throw new QuPathCloudException("Location must not be null!");
    }
    return STAGE + PROJECTS + queryBuilder.getProjectId() + LOCATIONS + queryBuilder.getLocationId()
        + DATASETS;
  }
} public class DicomStoresPathBuilder implements PathBuilder {

  private QueryBuilder queryBuilder;

  /**
   * Instantiates a new Dicom Stores path builder.
   *
   * @param queryBuilder the query builder with parameters
   */
  public DicomStoresPathBuilder(QueryBuilder queryBuilder) {
    this.queryBuilder = queryBuilder;
  }

  @Override
  public String toPath() throws QuPathCloudException {
    if (queryBuilder.getLocationId() == null) {
      throw new QuPathCloudException("Location must not be null!");
    }
    if (queryBuilder.getDatasetId() == null) {
      throw new QuPathCloudException("Dataset must not be null!");
    }
    return STAGE + PROJECTS + queryBuilder.getProjectId() + LOCATIONS + queryBuilder.getLocationId()
        + DATASETS + queryBuilder.getDatasetId() + DICOM_STORES;
  }
} public class FramePathBuilder implements PathBuilder {

  private QueryBuilder queryBuilder;

  /**
   * Instantiates a new Frame path builder.
   *
   * @param queryBuilder the query builder with parameters
   */
  public FramePathBuilder(QueryBuilder queryBuilder) {
    this.queryBuilder = queryBuilder;
  }

  @Override
  public String toPath() throws QuPathCloudException {
    if (queryBuilder.getLocationId() == null) {
      throw new QuPathCloudException("Location must not be null!");
    }
    if (queryBuilder.getDatasetId() == null) {
      throw new QuPathCloudException("Dataset must not be null!");
    }
    if (queryBuilder.getDicomStoreId() == null) {
      throw new QuPathCloudException("DicomStore must not be null!");
    }
    if (queryBuilder.getStudyId() == null) {
      throw new QuPathCloudException("Study must not be null!");
    }
    if (queryBuilder.getSeriesId() == null) {
      throw new QuPathCloudException("Series must not be null!");
    }
    if (queryBuilder.getInstanceId() == null) {
      throw new QuPathCloudException("Instance must not be null!");
    }
    if (queryBuilder.getFrameNumber() <= 0) {
      throw new QuPathCloudException("Frame must be > 0!");
    }
    return STAGE + PROJECTS + queryBuilder.getProjectId() + LOCATIONS + queryBuilder.getLocationId()
        + DATASETS + queryBuilder.getDatasetId() + DICOM_STORES + queryBuilder.getDicomStoreId() +
        DICOM_WEB + STUDIES + queryBuilder.getStudyId() + SERIES + queryBuilder.getSeriesId() +
        INSTANCES + queryBuilder.getInstanceId() + FRAMES + queryBuilder.getFrameNumber();
  }
} public class InstancesPathBuilder implements PathBuilder {

  private QueryBuilder queryBuilder;

  /**
   * Instantiates a new Instances path builder.
   *
   * @param queryBuilder the query builder with parameters
   */
  public InstancesPathBuilder(QueryBuilder queryBuilder) {
    this.queryBuilder = queryBuilder;
  }

  @Override
  public String toPath() throws QuPathCloudException {
    if (queryBuilder.getLocationId() == null) {
      throw new QuPathCloudException("Location must not be null!");
    }
    if (queryBuilder.getDatasetId() == null) {
      throw new QuPathCloudException("Dataset must not be null!");
    }
    if (queryBuilder.getDicomStoreId() == null) {
      throw new QuPathCloudException("DicomStore must not be null!");
    }
    String studyId = queryBuilder.getStudyId();
    String seriesId = queryBuilder.getSeriesId();
    if (studyId != null && seriesId != null) {
      return STAGE + PROJECTS + queryBuilder.getProjectId() + LOCATIONS + queryBuilder
          .getLocationId() + DATASETS + queryBuilder.getDatasetId() + DICOM_STORES +
          queryBuilder.getDicomStoreId() + DICOM_WEB + STUDIES + studyId + SERIES +
          seriesId + INSTANCES;
    } else {
      return STAGE + PROJECTS + queryBuilder.getProjectId() + LOCATIONS + queryBuilder
          .getLocationId() + DATASETS + queryBuilder.getDatasetId() + DICOM_STORES +
          queryBuilder.getDicomStoreId() + DICOM_WEB + INSTANCES;
    }
  }
} public class LocationsPathBuilder implements PathBuilder {

  private QueryBuilder queryBuilder;

  /**
   * Instantiates a new Locations path builder.
   *
   * @param queryBuilder the query builder with parameters
   */
  public LocationsPathBuilder(QueryBuilder queryBuilder) {
    this.queryBuilder = queryBuilder;
  }

  public String toPath() {
    return STAGE + PROJECTS + queryBuilder.getProjectId() + LOCATIONS;
  }
} public class QueryBuilder {

  private String projectId;
  private String locationId;
  private String datasetId;
  private String dicomStoreId;
  private String studyId;
  private String seriesId;
  private String instanceId;
  private int frameNumber;
  private List<Path> paths;
  private Path directory;
  private List<Location> locations;
  private List<Instance> instances;

  private QueryBuilder() {
  }

  /**
   * Instantiates a new Query builder.
   *
   * @param original the original
   */
  public QueryBuilder(QueryBuilder original) {
    this.projectId = original.projectId;
    this.locationId = original.locationId;
    this.datasetId = original.datasetId;
    this.dicomStoreId = original.dicomStoreId;
    this.studyId = original.studyId;
    this.seriesId = original.seriesId;
    this.instanceId = original.instanceId;
    this.frameNumber = original.frameNumber;
    this.paths = original.paths;
    this.directory = original.directory;
    this.locations = original.locations;
    this.instances = original.instances;
  }

  /**
   * For project query builder.
   *
   * @param projectId the Project ID
   * @return the query builder
   */
  public static QueryBuilder forProject(String projectId) {
    QueryBuilder builder = new QueryBuilder();
    builder.projectId = projectId;
    return builder;
  }

  /**
   * Sets Location ID.
   *
   * @param locationId the Location ID
   * @return the Location ID
   */
  public QueryBuilder setLocationId(String locationId) {
    this.locationId = locationId;
    return this;
  }

  /**
   * Sets Dataset ID.
   *
   * @param datasetId the Dataset ID
   * @return the Dataset ID
   */
  public QueryBuilder setDatasetId(String datasetId) {
    this.datasetId = datasetId;
    return this;
  }

  /**
   * Sets DICOM Store ID.
   *
   * @param dicomStoreId the DICOM Store ID
   * @return the DICOM Store ID
   */
  public QueryBuilder setDicomStoreId(String dicomStoreId) {
    this.dicomStoreId = dicomStoreId;
    return this;
  }

  /**
   * Sets Study ID.
   *
   * @param studyId the Study ID
   * @return the Study ID
   */
  public QueryBuilder setStudyId(String studyId) {
    this.studyId = studyId;
    return this;
  }

  /**
   * Sets Series ID.
   *
   * @param seriesId the Series ID
   * @return the Series ID
   */
  public QueryBuilder setSeriesId(String seriesId) {
    this.seriesId = seriesId;
    return this;
  }

  /**
   * Sets Instance ID.
   *
   * @param instanceId the Instance ID
   * @return the Instance ID
   */
  public QueryBuilder setInstanceId(String instanceId) {
    this.instanceId = instanceId;
    return this;
  }

  /**
   * Sets frame number.
   *
   * @param frameNumber the frame number
   * @return the frame number
   */
  public QueryBuilder setFrameNumber(int frameNumber) {
    this.frameNumber = frameNumber;
    return this;
  }

  /**
   * Sets paths.
   *
   * @param paths the paths
   * @return the paths
   */
  public QueryBuilder setPaths(List<Path> paths) {
    this.paths = paths;
    return this;
  }

  /**
   * Sets directory.
   *
   * @param directory the directory
   * @return the directory
   */
  public QueryBuilder setDirectory(Path directory) {
    this.directory = directory;
    return this;
  }

  /**
   * Sets instances.
   *
   * @param instances the instances
   * @return the instances
   */
  public QueryBuilder setInstances(List<Instance> instances) {
    this.instances = instances;
    return this;
  }

  /**
   * Sets locations.
   *
   * @param locations the locations
   * @return the locations
   */
  public QueryBuilder setLocations(List<Location> locations) {
    this.locations = locations;
    return this;
  }

  /**
   * Gets Project ID.
   *
   * @return the Project ID
   */
  String getProjectId() {
    return projectId;
  }

  /**
   * Gets Location ID.
   *
   * @return the Location ID
   */
  String getLocationId() {
    return locationId;
  }

  /**
   * Gets Dataset ID.
   *
   * @return the Dataset ID
   */
  public String getDatasetId() {
    return datasetId;
  }

  /**
   * Gets DICOM Store ID.
   *
   * @return the DICOM Store ID
   */
  public String getDicomStoreId() {
    return dicomStoreId;
  }

  /**
   * Gets Study ID.
   *
   * @return the Study ID
   */
  String getStudyId() {
    return studyId;
  }

  /**
   * Gets Series ID.
   *
   * @return the Series ID
   */
  String getSeriesId() {
    return seriesId;
  }

  /**
   * Gets Instance ID.
   *
   * @return the Instance ID
   */
  String getInstanceId() {
    return instanceId;
  }

  /**
   * Gets frame number.
   *
   * @return the frame number
   */
  int getFrameNumber() {
    return frameNumber;
  }

  /**
   * Gets paths.
   *
   * @return the paths
   */
  public List<Path> getPaths() {
    return paths;
  }

  /**
   * Gets directory.
   *
   * @return the directory
   */
  public Path getDirectory() {
    return directory;
  }

  /**
   * Gets instances.
   *
   * @return the instances
   */
  public List<Instance> getInstances() {
    return instances;
  }

  /**
   * Gets locations.
   *
   * @return the locations
   */
  public List<Location> getLocations() {
    return locations;
  }
} public class SeriesPathBuilder implements PathBuilder {

  private QueryBuilder queryBuilder;

  /**
   * Instantiates a new Series path builder.
   *
   * @param queryBuilder the query builder with parameters
   */
  public SeriesPathBuilder(QueryBuilder queryBuilder) {
    this.queryBuilder = queryBuilder;
  }

  @Override
  public String toPath() throws QuPathCloudException {
    if (queryBuilder.getLocationId() == null) {
      throw new QuPathCloudException("Location must not be null!");
    }
    if (queryBuilder.getDatasetId() == null) {
      throw new QuPathCloudException("Dataset must not be null!");
    }
    if (queryBuilder.getDicomStoreId() == null) {
      throw new QuPathCloudException("DicomStore must not be null!");
    }
    String studyId = queryBuilder.getStudyId();
    if (studyId != null) {
      return STAGE + PROJECTS + queryBuilder.getProjectId() + LOCATIONS + queryBuilder
          .getLocationId() + DATASETS + queryBuilder.getDatasetId() + DICOM_STORES +
          queryBuilder.getDicomStoreId() + DICOM_WEB + STUDIES + studyId + SERIES;
    } else {
      return STAGE + PROJECTS + queryBuilder.getProjectId() + LOCATIONS + queryBuilder
          .getLocationId() + DATASETS + queryBuilder.getDatasetId() + DICOM_STORES +
          queryBuilder.getDicomStoreId() + DICOM_WEB + SERIES;
    }
  }
} public class StudiesPathBuilder implements PathBuilder {

  private QueryBuilder queryBuilder;

  /**
   * Instantiates a new Studies path builder.
   *
   * @param queryBuilder the query builder with parameters
   */
  public StudiesPathBuilder(QueryBuilder queryBuilder) {
    this.queryBuilder = queryBuilder;
  }

  @Override
  public String toPath() throws QuPathCloudException {
    if (queryBuilder.getLocationId() == null) {
      throw new QuPathCloudException("Location must not be null!");
    }
    if (queryBuilder.getDatasetId() == null) {
      throw new QuPathCloudException("Dataset must not be null!");
    }
    if (queryBuilder.getDicomStoreId() == null) {
      throw new QuPathCloudException("DicomStore must not be null!");
    }
    return STAGE + PROJECTS + queryBuilder.getProjectId() + LOCATIONS + queryBuilder.getLocationId()
        + DATASETS + queryBuilder.getDatasetId() + DICOM_STORES + queryBuilder.getDicomStoreId() +
        DICOM_WEB + STUDIES;
  }
} public class Dataset {

  private String name;
  private String timeZone;

  /**
   * Gets Project ID.
   *
   * @return the Project ID
   */
  @JsonIgnore
  public String getProjectId() {
    String[] arr = name.split("/");
    return arr[arr.length - 5];
  }

  /**
   * Gets Location ID.
   *
   * @return the Location ID
   */
  @JsonIgnore
  public String getLocationId() {
    String[] arr = name.split("/");
    return arr[arr.length - 3];
  }

  /**
   * Gets Dataset ID.
   *
   * @return the Dataset ID
   */
  @JsonIgnore
  public String getDatasetId() {
    String[] arr = name.split("/");
    return arr[arr.length - 1];
  }

  /**
   * Gets name.
   *
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * Sets name.
   *
   * @param name the name
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * Sets name.
   *
   * @param projectId the Project ID
   * @param locationId the Location ID
   * @param datasetId the Dataset ID
   */
  public void setName(String projectId, String locationId, String datasetId) {
    name = "projects/" + projectId + "/locations/" + locationId + "/datasets/" + datasetId;
  }

  /**
   * Gets time zone.
   *
   * @return the time zone
   */
  public String getTimeZone() {
    return timeZone;
  }

  /**
   * Sets time zone.
   *
   * @param timeZone the time zone
   */
  public void setTimeZone(String timeZone) {
    this.timeZone = timeZone;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    Dataset dataset = (Dataset) o;
    return name.equals(dataset.name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(name);
  }
} public class Datasets {

  private List<Dataset> datasets = new ArrayList<>();
  private String nextPageToken;

  /**
   * Gets the list of {@link com.quantumsoft.qupathcloud.entities.Dataset}.
   *
   * @return the list of {@link com.quantumsoft.qupathcloud.entities.Dataset}
   */
  public List<Dataset> getDatasets() {
    return datasets;
  }

  /**
   * Gets next page token.
   *
   * @return the next page token
   */
  public String getNextPageToken() {
    return nextPageToken;
  }
} public class DicomStore {

  private String name;

  /**
   * Gets Project ID.
   *
   * @return the Project ID
   */
  @JsonIgnore
  public String getProjectId() {
    String[] arr = name.split("/");
    return arr[arr.length - 7];
  }

  /**
   * Gets Location ID.
   *
   * @return the Location ID
   */
  @JsonIgnore
  public String getLocationId() {
    String[] arr = name.split("/");
    return arr[arr.length - 5];
  }

  /**
   * Gets Dataset ID.
   *
   * @return the Dataset ID
   */
  @JsonIgnore
  public String getDatasetId() {
    String[] arr = name.split("/");
    return arr[arr.length - 3];
  }

  /**
   * Gets DICOM Store ID.
   *
   * @return the DICOM Store ID
   */
  @JsonIgnore
  public String getDicomStoreId() {
    String[] arr = name.split("/");
    return arr[arr.length - 1];
  }

  /**
   * Gets name.
   *
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * Sets name.
   *
   * @param name the name
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * Sets name.
   *
   * @param projectId    the Project ID
   * @param locationId   the Location ID
   * @param datasetId    the Dataset ID
   * @param dicomStoreId the DICOM Store ID
   */
  public void setName(String projectId, String locationId, String datasetId, String dicomStoreId) {
    name = "projects/" + projectId + "/locations/" + locationId + "/datasets/" + datasetId
        + "/dicomStores/" +
        dicomStoreId;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    DicomStore that = (DicomStore) o;
    return Objects.equals(name, that.name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(name);
  }
} public class DicomStores {

  private List<DicomStore> dicomStores = new ArrayList<>();
  private String nextPageToken;

  /**
   * Gets the list of {@link com.quantumsoft.qupathcloud.entities.DicomStore}.
   *
   * @return the list of {@link com.quantumsoft.qupathcloud.entities.DicomStore}
   */
  public List<DicomStore> getDicomStores() {
    return dicomStores;
  }

  /**
   * Gets next page token.
   *
   * @return the next page token
   */
  public String getNextPageToken() {
    return nextPageToken;
  }
} public class Location {

  private String name;
  private String locationId;

  /**
   * Gets Project ID.
   *
   * @return the Project ID
   */
  @JsonIgnore
  public String getProjectId() {
    String[] arr = name.split("/");
    return arr[arr.length - 3];
  }

  /**
   * Gets Location ID.
   *
   * @return the Location ID
   */
  public String getLocationId() {
    return locationId;
  }

  /**
   * Sets Location ID.
   *
   * @param locationId the Location ID
   */
  public void setLocationId(String locationId) {
    this.locationId = locationId;
  }

  /**
   * Gets name.
   *
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * Sets name.
   *
   * @param name the name
   */
  public void setName(String name) {
    this.name = name;
  }
} public class Locations {

  private List<Location> locations = new ArrayList<>();
  private String nextPageToken;

  /**
   * Gets the list of {@link com.quantumsoft.qupathcloud.entities.Location}.
   *
   * @return the list of {@link com.quantumsoft.qupathcloud.entities.Location}
   */
  public List<Location> getLocations() {
    return locations;
  }

  /**
   * Gets next page token.
   *
   * @return the next page token
   */
  public String getNextPageToken() {
    return nextPageToken;
  }
} public class Project {

  private String projectId;
  private String name;

  /**
   * Gets Project ID.
   *
   * @return the Project ID
   */
  public String getProjectId() {
    return projectId;
  }

  /**
   * Sets Project ID.
   *
   * @param projectId the Project ID
   */
  public void setProjectId(String projectId) {
    this.projectId = projectId;
  }

  /**
   * Gets name.
   *
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * Sets name.
   *
   * @param name the name
   */
  public void setName(String name) {
    this.name = name;
  }
} public class Projects {

  private List<Project> projects = new ArrayList<>();
  private String nextPageToken;

  /**
   * Gets the list of {@link com.quantumsoft.qupathcloud.entities.Project}.
   *
   * @return the list of {@link com.quantumsoft.qupathcloud.entities.Project}
   */
  public List<Project> getProjects() {
    return projects;
  }

  /**
   * Gets next page token.
   *
   * @return the next page token
   */
  public String getNextPageToken() {
    return nextPageToken;
  }
} public class Series {

  @JsonProperty("0020000D")
  private DicomAttribute<String> studyInstanceUID;
  @JsonProperty("0020000E")
  private DicomAttribute<String> seriesInstanceUID;
  @JsonProperty("0008103E")
  private DicomAttribute<String> seriesDescription;
  @Deprecated
  @JsonProperty("00204000")
  private DicomAttribute<String> imageComments;
  @JsonProperty("00080060")
  private DicomAttribute<String> modality;

  /**
   * Gets Study Instance UID.
   *
   * @return the Study Instance UID
   */
  public DicomAttribute<String> getStudyInstanceUID() {
    return studyInstanceUID;
  }

  /**
   * Sets Study Instance UID.
   *
   * @param studyInstanceUID the Study Instance UID
   */
  public void setStudyInstanceUID(DicomAttribute<String> studyInstanceUID) {
    this.studyInstanceUID = studyInstanceUID;
  }

  /**
   * Gets Series Instance UID.
   *
   * @return the Series Instance UID
   */
  public DicomAttribute<String> getSeriesInstanceUID() {
    return seriesInstanceUID;
  }

  /**
   * Sets Series Instance UID.
   *
   * @param seriesInstanceUID the Series Instance UID
   */
  public void setSeriesInstanceUID(DicomAttribute<String> seriesInstanceUID) {
    this.seriesInstanceUID = seriesInstanceUID;
  }

  /**
   * Gets Series Description.
   *
   * @return the Series Description
   */
  public DicomAttribute<String> getSeriesDescription() {
    if (seriesDescription == null || seriesDescription.isEmpty()) {
      String temp = studyInstanceUID.getValue1() + seriesInstanceUID.getValue1();
      seriesDescription = new DicomAttribute<>();
      seriesDescription.setValue(new String[]{String.valueOf(temp.hashCode())});
    }
    return seriesDescription;
  }

  /**
   * Sets Series Description.
   *
   * @param imageComments the image comments
   */
  public void setSeriesDescription(DicomAttribute<String> imageComments) {
    this.seriesDescription = imageComments;
  }

  /**
   * Gets Modality.
   *
   * @return the Modality
   */
  public DicomAttribute<String> getModality() {
    return modality;
  }

  /**
   * Sets Modality.
   *
   * @param modality the Modality
   */
  public void setModality(DicomAttribute<String> modality) {
    this.modality = modality;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    Series series = (Series) o;
    return Objects.equals(studyInstanceUID, series.studyInstanceUID) &&
        Objects.equals(seriesInstanceUID, series.seriesInstanceUID) &&
        Objects.equals(seriesDescription, series.seriesDescription);
  }

  @Override
  public int hashCode() {
    return Objects.hash(studyInstanceUID, seriesInstanceUID, seriesDescription);
  }
} public class Study {

  @JsonProperty("0020000D")
  private DicomAttribute<String> studyInstanceUID;

  /**
   * Gets Study Instance UID.
   *
   * @return the Study Instance UID
   */
  public DicomAttribute<String> getStudyInstanceUID() {
    return studyInstanceUID;
  }

  /**
   * Sets Study Instance UID.
   *
   * @param studyInstanceUID the Study Instance UID
   */
  public void setStudyInstanceUID(DicomAttribute<String> studyInstanceUID) {
    this.studyInstanceUID = studyInstanceUID;
  }
} @JsonInclude(JsonInclude.Include.NON_NULL)
public class Instance {

  private static final String TILED_FULL = "TILED_FULL";

  @JsonProperty("0020000D")
  private DicomAttribute<String> studyInstanceUID; //required includefield parameter
  @JsonProperty("0020000E")
  private DicomAttribute<String> seriesInstanceUID; //required includefield parameter
  @JsonProperty("00080018")
  private DicomAttribute<String> sopInstanceUID;
  @JsonProperty("00080060")
  private DicomAttribute<String> modality;
  @JsonProperty("00480006")
  private DicomAttribute<Integer> totalPixelMatrixColumns; //required includefield parameter
  @JsonProperty("00480007")
  private DicomAttribute<Integer> totalPixelMatrixRows; //required includefield parameter
  @JsonProperty("00280010")
  private DicomAttribute<Integer> rows;
  @JsonProperty("00280011")
  private DicomAttribute<Integer> columns;
  @JsonProperty("52009230")
  private DicomAttribute<ObjectOfPerframeFunctionalGroupsSequence> perframeFunctionalGroupsSequence; //required includefield parameter
  @JsonProperty("00080012")
  private DicomAttribute<String> instanceCreationDate;
  @JsonProperty("00080013")
  private DicomAttribute<String> instanceCreationTime;
  @JsonProperty("00080201")
  private DicomAttribute<String> timezoneOffsetFromUTC;
  @JsonProperty("01000424")
  private DicomAttribute<String> sopAuthorizationComment;
  @JsonProperty("00209311")
  private DicomAttribute<String> dimensionOrganizationType;
  @JsonProperty("00209161")
  private DicomAttribute<String> concatenationUID;
  @JsonProperty("00209228")
  private DicomAttribute<Integer> concatenationFrameOffsetNumber;
  @JsonProperty("00280008")
  private DicomAttribute<Integer> numberOfFrames;


  /**
   * Gets Study Instance UID.
   *
   * @return the Study Instance UID
   */
  public DicomAttribute<String> getStudyInstanceUID() {
    return studyInstanceUID;
  }

  /**
   * Sets Study Instance UID.
   *
   * @param studyInstanceUID the Study Instance UID
   */
  public void setStudyInstanceUID(DicomAttribute<String> studyInstanceUID) {
    this.studyInstanceUID = studyInstanceUID;
  }

  /**
   * Gets Series Instance UID.
   *
   * @return the Series Instance UID
   */
  public DicomAttribute<String> getSeriesInstanceUID() {
    return seriesInstanceUID;
  }

  /**
   * Sets Series Instance UID.
   *
   * @param seriesInstanceUID the Series Instance UID
   */
  public void setSeriesInstanceUID(DicomAttribute<String> seriesInstanceUID) {
    this.seriesInstanceUID = seriesInstanceUID;
  }

  /**
   * Gets SOP Instance UID.
   *
   * @return the SOP Instance UID
   */
  public DicomAttribute<String> getSopInstanceUID() {
    return sopInstanceUID;
  }

  /**
   * Sets SOP Instance UID.
   *
   * @param sopInstanceUID the SOP Instance UID
   */
  public void setSopInstanceUID(DicomAttribute<String> sopInstanceUID) {
    this.sopInstanceUID = sopInstanceUID;
  }

  /**
   * Gets Modality.
   *
   * @return the Modality
   */
  public DicomAttribute<String> getModality() {
    return modality;
  }

  /**
   * Sets Modality.
   *
   * @param modality the Modality
   */
  public void setModality(DicomAttribute<String> modality) {
    this.modality = modality;
  }

  /**
   * Gets Total Pixel Matrix Columns.
   *
   * @return the Total Pixel Matrix Columns
   */
  public DicomAttribute<Integer> getTotalPixelMatrixColumns() {
    return totalPixelMatrixColumns;
  }

  /**
   * Sets Total Pixel Matrix Columns.
   *
   * @param totalPixelMatrixColumns the Total Pixel Matrix Columns
   */
  public void setTotalPixelMatrixColumns(DicomAttribute<Integer> totalPixelMatrixColumns) {
    this.totalPixelMatrixColumns = totalPixelMatrixColumns;
  }

  /**
   * Gets Total Pixel Matrix Rows.
   *
   * @return the Total Pixel Matrix Rows
   */
  public DicomAttribute<Integer> getTotalPixelMatrixRows() {
    return totalPixelMatrixRows;
  }

  /**
   * Sets Total Pixel Matrix Rows.
   *
   * @param totalPixelMatrixRows the Total Pixel Matrix Rows
   */
  public void setTotalPixelMatrixRows(DicomAttribute<Integer> totalPixelMatrixRows) {
    this.totalPixelMatrixRows = totalPixelMatrixRows;
  }

  /**
   * Gets Rows.
   *
   * @return the Rows
   */
  public DicomAttribute<Integer> getRows() {
    return rows;
  }

  /**
   * Sets Rows.
   *
   * @param rows the Rows
   */
  public void setRows(DicomAttribute<Integer> rows) {
    this.rows = rows;
  }

  /**
   * Gets Columns.
   *
   * @return the Columns
   */
  public DicomAttribute<Integer> getColumns() {
    return columns;
  }

  /**
   * Sets Columns.
   *
   * @param columns the Columns
   */
  public void setColumns(DicomAttribute<Integer> columns) {
    this.columns = columns;
  }

  /**
   * Gets Per-frame Functional Groups Sequence.
   *
   * @return the Per-frame Functional Groups Sequence
   */
  public DicomAttribute<ObjectOfPerframeFunctionalGroupsSequence> getPerframeFunctionalGroupsSequence() {
    return perframeFunctionalGroupsSequence;
  }

  /**
   * Sets Per-frame Functional Groups Sequence.
   *
   * @param perframeFunctionalGroupsSequence the Per-frame Functional Groups Sequence
   */
  public void setPerframeFunctionalGroupsSequence(
      DicomAttribute<ObjectOfPerframeFunctionalGroupsSequence> perframeFunctionalGroupsSequence) {
    this.perframeFunctionalGroupsSequence = perframeFunctionalGroupsSequence;
  }

  /**
   * Gets creation date.
   *
   * @return the creation date
   * @throws QuPathCloudException if ParseException occurs
   */
  @JsonIgnore
  public Date getCreationDate() throws QuPathCloudException {
    String format = "yyyyMMdd HHmmss.SSS Z";
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
    try {
      String dateString = instanceCreationDate.getValue1() + " " + instanceCreationTime.getValue1()
          + " " + timezoneOffsetFromUTC.getValue1();
      return simpleDateFormat.parse(dateString);
    } catch (ParseException e) {
      throw new QuPathCloudException(e);
    }
  }

  /**
   * Gets Instance Creation Date.
   *
   * @return the Instance Creation Date
   */
  public DicomAttribute<String> getInstanceCreationDate() {
    return instanceCreationDate;
  }

  /**
   * Gets Instance Creation Time.
   *
   * @return the Instance Creation Time
   */
  public DicomAttribute<String> getInstanceCreationTime() {
    return instanceCreationTime;
  }

  /**
   * Gets timezone offset from UTC.
   *
   * @return the timezone offset from UTC
   */
  public DicomAttribute<String> getTimezoneOffsetFromUTC() {
    return timezoneOffsetFromUTC;
  }

  /**
   * Gets SOP Authorization Comment.
   *
   * @return the SOP Authorization Comment
   */
  public DicomAttribute<String> getSopAuthorizationComment() {
    return sopAuthorizationComment;
  }

  /**
   * Gets Dimension Organization Type.
   *
   * @return the Dimension Organization Type
   */
  public DicomAttribute<String> getDimensionOrganizationType() {
    return dimensionOrganizationType;
  }

  /**
   * Is full tiled boolean.
   *
   * @return the boolean
   */
  @JsonIgnore
  public boolean isFullTiled() {
    if (dimensionOrganizationType == null) {
      return false;
    }
    String[] values = dimensionOrganizationType.getValue();
    if (values.length == 0) {
      return false;
    }
    return values[0].equals(TILED_FULL);
  }

  /**
   * Gets Concatenation UID.
   *
   * @return the Concatenation UID
   */
  public DicomAttribute<String> getConcatenationUID() {
    return concatenationUID;
  }

  /**
   * Gets Concatenation Frame Offset Number.
   *
   * @return the Concatenation Frame Offset Number
   */
  public DicomAttribute<Integer> getConcatenationFrameOffsetNumber() {
    return concatenationFrameOffsetNumber;
  }

  /**
   * Gets Number of Frames.
   *
   * @return the Number of Frames
   */
  public DicomAttribute<Integer> getNumberOfFrames() {
    return numberOfFrames;
  }
} public class QuPathCloudException extends Exception {

  /**
   * Instantiates a new QuPath cloud exception.
   *
   * @param message the message
   */
  public QuPathCloudException(String message) {
    super(message);
  }

  /**
   * Instantiates a new QuPath cloud exception.
   *
   * @param e the exception
   */
  public QuPathCloudException(Throwable e) {
    super(e);
  }
} public class OAuth20 {

  private static final Logger LOGGER = LogManager.getLogger();
  private static final String CLIENT_SECRETS_FILE_NAME = "client_secrets.json";
  private static final Path STORE_DIRECTORY = Paths.get(".store/oauth20-0.2.0-m2");
  private static final List<String> SCOPES = Arrays.asList(
      "https://www.googleapis.com/auth/cloud-healthcare",
      "https://www.googleapis.com/auth/cloudplatformprojects.readonly");
  private static final String USER_ID = "user";
  private static final int TIME_WHEN_REFRESH_TOKEN_IN_SECONDS = 60;
  private final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
  private final File DATA_STORE_DIR;
  private AuthorizationCodeInstalledApp authenticator;
  private Credential credential;

  /**
   * Instantiates a new OAuth20.
   *
   * @param baseQupathDirectory the base QuPath directory
   */
  public OAuth20(Path baseQupathDirectory) {
    DATA_STORE_DIR = baseQupathDirectory.resolve(STORE_DIRECTORY).toFile();
  }

  /**
   * Gets credential.
   *
   * @return the credential
   * @throws QuPathCloudException if an error occurs
   */
  public synchronized Credential getCredential() throws QuPathCloudException {
    try {
      if (authenticator == null) {
        login();
      }
      if (credential.getExpiresInSeconds() < TIME_WHEN_REFRESH_TOKEN_IN_SECONDS) {
        credential.refreshToken();
        credential = authenticator.authorize(USER_ID);
      }
    } catch (IOException | GeneralSecurityException e) {
      throw new QuPathCloudException(e);
    }
    LOGGER.trace("Got credential");
    return credential;
  }

  /**
   * Invalidates credentials.
   *
   * @throws IOException if IOException occurs
   */
  public void invalidateCredentials() throws IOException {
    if (authenticator != null) {
      authenticator.getReceiver().stop();
      authenticator.getFlow().getCredentialDataStore().clear();
      authenticator = null;
    }
  }

  private AuthorizationCodeInstalledApp getAuthenticator()
      throws QuPathCloudException, IOException, GeneralSecurityException {
    InputStream is = getClass().getClassLoader().getResourceAsStream(CLIENT_SECRETS_FILE_NAME);
    if (is == null) {
      throw new QuPathCloudException("Failed to load " + CLIENT_SECRETS_FILE_NAME);
    }
    GoogleClientSecrets clientSecrets = GoogleClientSecrets
        .load(JSON_FACTORY, new InputStreamReader(is, StandardCharsets.UTF_8));
    Details details = clientSecrets.getDetails();
    String clientId = details.getClientId();
    String clientSecret = details.getClientSecret();
    if (clientId.length() == 0 || clientSecret.length() == 0) {
      throw new QuPathCloudException("Empty client secret or client id!");
    }
    HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    FileDataStoreFactory dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);
    GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow
        .Builder(httpTransport, JSON_FACTORY, clientSecrets, SCOPES)
        .setDataStoreFactory(dataStoreFactory)
        .addRefreshListener(new DataStoreCredentialRefreshListener(USER_ID, dataStoreFactory))
        .setAccessType("offline")
        .setApprovalPrompt("force")
        .build();
    Repository.INSTANCE.getIsLoggedInProperty().set(false);
    return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver());
  }

  private void login() throws QuPathCloudException, IOException, GeneralSecurityException {
    LOGGER.debug("Login user");
    authenticator = getAuthenticator();
    authorize();
  }

  private void authorize() throws IOException {
    try {
      credential = authenticator.authorize(USER_ID);
    } catch (NullPointerException e) {
      //nope
    }
  }
}
import static com.quantumsoft.qupathcloud.dao.Constants.APPLICATION_JSON_CHARSET_UTF8;
import static com.quantumsoft.qupathcloud.dao.Constants.BEARER;
import static com.quantumsoft.qupathcloud.dao.Constants.CLOUD_RESOURCE_MANAGER_HOST;
import static com.quantumsoft.qupathcloud.dao.Constants.HEALTHCARE_HOST;
import static com.quantumsoft.qupathcloud.dao.Constants.INSTANCES;
import static com.quantumsoft.qupathcloud.dao.Constants.MULTIPART_RELATED_TYPE_IMAGE_JPEG_TRANSFER_SYNTAX;
import static com.quantumsoft.qupathcloud.dao.Constants.PARAM_DATASET_ID;
import static com.quantumsoft.qupathcloud.dao.Constants.PARAM_DICOM_STORE_ID;
import static com.quantumsoft.qupathcloud.dao.Constants.PARAM_INCLUDE_FIELD;
import static com.quantumsoft.qupathcloud.dao.Constants.PARAM_PAGE_TOKEN;
import static com.quantumsoft.qupathcloud.dao.Constants.PATH_TO_PROJECTS;
import static com.quantumsoft.qupathcloud.dao.Constants.SCHEME;
import static com.quantumsoft.qupathcloud.dao.Constants.SERIES;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_CONCATENATION_FRAME_OFFSET_NUMBER;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_CONCATENATION_UID;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_DIMENSION_ORGANIZATION_TYPE;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_IMAGE_COMMENTS;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_INSTANCE_CREATION_DATE;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_INSTANCE_CREATION_TIME;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_MODALITY;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_NUMBER_OF_FRAMES;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_PER_FRAME_FUNCTIONAL_GROUP_SEQUENCE;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_SERIES_INSTANCE_UID;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_SOP_AUTHORIZATHION_COMMENT;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_STUDY_INSTANCE_UID;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_TIMEZONE_OFFSET_FROM_UTC;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_TOTAL_PIXEL_MATRIX_COLUMNS;
import static com.quantumsoft.qupathcloud.dao.Constants.VALUE_PARAM_TOTAL_PIXEL_MATRIX_ROWS;
import static com.quantumsoft.qupathcloud.exception.Errors.FAILED_HTTP;
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.HttpHeaders.AUTHORIZATION;
import static org.apache.http.HttpHeaders.CONTENT_TYPE;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.http.HttpStatusCodes;
import com.quantumsoft.qupathcloud.dao.spec.DatasetsPathBuilder;
import com.quantumsoft.qupathcloud.dao.spec.DicomStoresPathBuilder;
import com.quantumsoft.qupathcloud.dao.spec.FramePathBuilder;
import com.quantumsoft.qupathcloud.dao.spec.InstancesPathBuilder;
import com.quantumsoft.qupathcloud.dao.spec.LocationsPathBuilder;
import com.quantumsoft.qupathcloud.dao.spec.QueryBuilder;
import com.quantumsoft.qupathcloud.dao.spec.SeriesPathBuilder;
import com.quantumsoft.qupathcloud.dao.spec.StudiesPathBuilder;
import com.quantumsoft.qupathcloud.entities.Dataset;
import com.quantumsoft.qupathcloud.entities.Datasets;
import com.quantumsoft.qupathcloud.entities.DicomStore;
import com.quantumsoft.qupathcloud.entities.DicomStores;
import com.quantumsoft.qupathcloud.entities.Location;
import com.quantumsoft.qupathcloud.entities.Locations;
import com.quantumsoft.qupathcloud.entities.Project;
import com.quantumsoft.qupathcloud.entities.Projects;
import com.quantumsoft.qupathcloud.entities.Series;
import com.quantumsoft.qupathcloud.entities.Study;
import com.quantumsoft.qupathcloud.entities.instance.Instance;
import com.quantumsoft.qupathcloud.exception.QuPathCloudException;
import com.quantumsoft.qupathcloud.oauth20.OAuth20;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.imageio.ImageIO;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
// Copyright (C) 2019 Google LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package com.quantumsoft.qupathcloud.dao;



/**
 * Implementation of Cloud dao for access to Healthcare API.
 */
public class CloudDaoImpl extends CloudDao {

  private static final String DCM_EXTENSION = "dcm";
  private static final int THREADS_COUNT = 4;
  private static final int R = 0x0d; // "\r"
  private static final int N = 0x0a; // "\n"

  /**
   * Instantiates a new Cloud dao.
   *
   * @param oAuth20 the o auth 20
   */
  public CloudDaoImpl(OAuth20 oAuth20) {
    super(oAuth20);
  }

  @Override
  public List<Project> getProjects() throws QuPathCloudException {
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(CLOUD_RESOURCE_MANAGER_HOST)
        .setPath(PATH_TO_PROJECTS);
    Projects projectsOnPage =
        createRequestForObjectList(uriBuilder, new TypeReference<Projects>() {});
    List<Project> projectList = new ArrayList<>(projectsOnPage.getProjects());
    String nextPageToken = projectsOnPage.getNextPageToken();
    while (nextPageToken != null) {
      uriBuilder.addParameter(PARAM_PAGE_TOKEN, nextPageToken);
      projectsOnPage = createRequestForObjectList(uriBuilder, new TypeReference<Projects>() {});
      projectList.addAll(projectsOnPage.getProjects());
      nextPageToken = projectsOnPage.getNextPageToken();
    }
    return projectList;
  }

  @Override
  public List<Location> getLocations(QueryBuilder queryBuilder) throws QuPathCloudException {
    LocationsPathBuilder locationsPathBuilder = new LocationsPathBuilder(queryBuilder);
    URIBuilder uriBuilder = new URIBuilder()
        .setScheme(SCHEME)
        .setHost(HEALTHCARE_HOST)
        .setPath(locationsPathBuilder.toPath());
    return createRequestForObjectList(uriBuilder, new TypeReference<Locations>() {}).getLocations();
  }

  @Override
  public List<Dataset> getDatasetsInAllLocations(QueryBuilder queryBuilder)
      throws QuPathCloudException {
    List<Dataset> datasetList = new ArrayList<>();
    for (Location location : queryBuilder.getLocations()) {
      queryBuilder.setLocationId(location.getLocationId());
      List<Dataset> datasetsInLocation = getDatasetsList(queryBuilder);
      datasetList.addAll(datasetsInLocation);
    }
    return datasetList;
  }

  @Override
  public List<DicomStore> getDicomStores(QueryBuilder queryBuilder)