/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.security.equity;
import java.io.Serializable;
import java.util.Collection;
import java.util.regex.Pattern;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
/**
* Representation of a GICS code.
* <p>
* A Global Industry Classification Standard code (GICS) is an 8 digit code
* used to identify the sectors and industries that a company operates in.
* <p>
* The 8 digits are divided into 4 digit-pairs representing a description hierarchy:
* <ul>
* <li>Sector
* <li>Industry-group
* <li>Industry
* <li>Sub-Industry
* </ul>
* For example, "Highways and Railtracks" is defined as follows:
* <ul>
* <li>Sector - Industrial - code 20
* <li>Industry group - Transportation - code 2030
* <li>Industry - Transportation infrastructure - code 203050
* <li>Sub-Industry - Highways and Railtracks - code 20305020
* </ul>
* <p>
* GICSCode is immutable and thread-safe.
*/
public final class GICSCode implements Serializable {
/** Serialization version. */
private static final long serialVersionUID = 1L;
/** Pattern for the code. */
private static final Pattern FORMAT = Pattern.compile("([1-9][0-9]){1,4}");
/**
* The code.
*/
private final String _code;
/**
* Obtains a {@code GICSCode} instance from the combined code.
* <p>
* The code specified must follow the GICS code standard, being a number
* between 1 and 99999999 inclusive where no two digit part is 0.
* The number is not validated against known values.
*
* @param code the value from 10 to 99999999 inclusive
* @return the GICS instance, not null
* @throws IllegalArgumentException if the value is invalid
*/
@FromString
public static GICSCode of(final String code) {
if (FORMAT.matcher(code).matches() == false) {
throw new IllegalArgumentException("Invalid code : " + code);
}
return new GICSCode(code);
}
/**
* Obtains a {@code GICSCode} instance from the combined code.
* <p>
* The code specified must follow the GICS code standard.
* The number must be between 10 and 99999999 inclusive where each digit-pair
* must be a number from 10 to 99.
* The number is not validated against known values in the standard.
*
* @param code the value from 1 to 99999999 inclusive
* @return the GICS instance, not null
* @throws IllegalArgumentException if the value is invalid
*/
public static GICSCode of(final int code) {
if ((code < 10) || (code > 99999999)) {
throw new IllegalArgumentException("Code out of range: " + code);
}
return GICSCode.of(Integer.toString(code));
}
/**
* Creates an instance with a specific code.
*
* @param code the GICS code, from 10 to 99999999
*/
private GICSCode(final String code) {
_code = code;
}
//-------------------------------------------------------------------------
/**
* Gets the full code.
* <p>
* The combined code will consist of the sector, group, industry and sub-industry parts.
* The returned length will be 2, 4, 6, or 8 characters long.
* For example, if the code represents only a sector then the value will be from 10 to 99.
*
* @return the combined code, from 10 to 99999999 inclusive
*/
@ToString
public String getCode() {
return _code;
}
/**
* Gets the combined code as an {@code int}.
* <p>
* The combined code will consist of the sector, group, industry and sub-industry parts.
* This is the equivalent of {@link #getCode()}.
*
* @return the combined code, from 10 to 99999999 inclusive
*/
public int getCodeInt() {
return Integer.parseInt(getCode());
}
/**
* Gets the best available description.
*
* @return the description, or "Unknown" if not found
*/
public String getDescription() {
switch (getCode().length()) {
case 2:
return getSectorDescription();
case 4:
return getIndustryGroupDescription();
case 6:
return getIndustryDescription();
case 8:
return getSubIndustryDescription();
}
return "Unknown";
}
//-------------------------------------------------------------------------
/**
* Gets the sector code.
* <p>
* The sector code is the most important part of the classification.
* It is the first two digits of the code.
*
* @return the sector code, from 10 to 99
*/
public String getSectorCode() {
return getCode().length() >= 2 ? getCode().substring(0, 2) : "";
}
/**
* Gets the sector code as an {@code int}.
* <p>
* The sector code is the most important part of the classification.
* It is the first two digits of the code.
* This is the equivalent of {@link #getSectorCode()}.
*
* @return the sector code, from 10 to 99
*/
public int getSectorCodeInt() {
return Integer.parseInt(getSectorCode());
}
/**
* Gets the sector description.
*
* @return the description of the sector, or "Unknown" if not found
*/
public String getSectorDescription() {
String description = GICSCodeDescription.INSTANCE.getDescription(getSectorCode());
return description != null ? description : "Unknown";
}
/**
* Gets all the sectors.
*
* @return a collection of all the sectors
*/
public static Collection<String> getAllSectorDescriptions() {
return GICSCodeDescription.INSTANCE.getAllSectorDescriptions();
}
//-------------------------------------------------------------------------
/**
* Gets the industry-group code.
* <p>
* The industry-group code is the second most important part of the classification.
* It is the first four digits of the code.
*
* @return the industry-group code, from 1010 to 9999, empty if no industry-group
*/
public String getIndustryGroupCode() {
return getCode().length() >= 4 ? getCode().substring(0, 4) : "";
}
/**
* Gets the industry-group code as an {@code int}.
* <p>
* The industry-group code is the second most important part of the classification.
* It is the first four digits of the code.
* This is the equivalent of {@link #getIndustryGroupCode()}.
*
* @return the industry-group code, from 1010 to 9999, 0 if no industry-group
*/
public int getIndustryGroupCodeInt() {
if (getCode().length() < 4) {
return 0;
}
return Integer.parseInt(getIndustryGroupCode());
}
/**
* Gets the industry-group description.
*
* @return the description of the industry-group, "Unknown" if not found, empty if no industry-group
*/
public String getIndustryGroupDescription() {
if (getCode().length() < 4) {
return "";
}
String description = GICSCodeDescription.INSTANCE.getDescription(getIndustryGroupCode());
return description != null ? description : "Unknown";
}
/**
* Gets all the industry group descriptions.
*
* @return a collection of all the industry group descriptions
*/
public static Collection<String> getAllIndustryGroupDescriptions() {
return GICSCodeDescription.INSTANCE.getAllIndustryGroupDescriptions();
}
//-------------------------------------------------------------------------
/**
* Gets the industry code.
* <p>
* The industry code is the third most important part of the classification.
* It is the first six digits of the code.
*
* @return the industry code, from 101010 to 999999, empty if no industry
*/
public String getIndustryCode() {
return getCode().length() >= 6 ? getCode().substring(0, 6) : "";
}
/**
* Gets the industry code as an {@code int}.
* <p>
* The industry code is the third most important part of the classification.
* It is the first six digits of the code.
* This is the equivalent of {@link #getIndustryCode()}.
*
* @return the industry code, from 101010 to 999999, 0 if no industry
*/
public int getIndustryCodeInt() {
if (getCode().length() < 6) {
return 0;
}
return Integer.parseInt(getIndustryCode());
}
/**
* Gets the industry description.
*
* @return the description of the industry, "Unknown" if not found, empty if no industry
*/
public String getIndustryDescription() {
if (getCode().length() < 6) {
return "";
}
String description = GICSCodeDescription.INSTANCE.getDescription(getIndustryCode());
return description != null ? description : "Unknown";
}
/**
* Gets all the industry descriptions.
*
* @return a collection of all the industry descriptions
*/
public static Collection<String> getAllIndustryDescriptions() {
return GICSCodeDescription.INSTANCE.getAllIndustryDescriptions();
}
//-------------------------------------------------------------------------
/**
* Gets the sub-industry code.
* <p>
* The group code is the least important part of the classification.
* It is the first eight digits of the code.
*
* @return the sub-industry code, from 10101010 to 99999999
*/
public String getSubIndustryCode() {
return getCode().length() == 8 ? getCode() : "";
}
/**
* Gets the sub-industry code as an {@code int}.
* <p>
* The group code is the least important part of the classification.
* It is the first eight digits of the code.
* This is the equivalent of {@link #getSubIndustryCode()}.
*
* @return the sub-industry code, from 10101010 to 99999999, 0 if no sub-industry
*/
public int getSubIndustryCodeInt() {
if (getCode().length() < 8) {
return 0;
}
return Integer.parseInt(getSubIndustryCode());
}
/**
* Gets the sub-industry description.
*
* @return the description of the sub-industry, "Unknown" if not found, empty if no sub-industry
*/
public String getSubIndustryDescription() {
if (getCode().length() < 8) {
return "";
}
String description = GICSCodeDescription.INSTANCE.getDescription(getSubIndustryCode());
return description != null ? description : "Unknown";
}
/**
* Gets all the sub-industry descriptions.
*
* @return a collection of all the sub-industry descriptions
*/
public static Collection<String> getAllSubIndustryDescriptions() {
return GICSCodeDescription.INSTANCE.getAllSubIndustryDescriptions();
}
//-------------------------------------------------------------------------
/**
* Checks if the code is a complete 8 digit sub-industry code.
*
* @return true if complete 8 digit code
*/
public boolean isComplete() {
return getCode().length() == 8;
}
/**
* Checks if the code is a partial code of less than 8 digits.
*
* @return true if less than the complete 8 digit code
*/
public boolean isPartial() {
return getCode().length() < 8;
}
//-------------------------------------------------------------------------
/**
* Obtains a code object for the sector only.
* <p>
* This returns a {@code GICSCode} representing just the sector.
*
* @return the object representing the sector, not null
*/
public GICSCode toSector() {
return GICSCode.of(getSectorCode());
}
/**
* Obtains a code object for the industry-group only.
* <p>
* This returns a {@code GICSCode} representing just the industry-group.
* Null is returned if this object represents a sector.
*
* @return the object representing the industry-group, null if no industry-group code
*/
public GICSCode toIndustryGroup() {
if (getCode().length() < 4) {
return null;
}
return GICSCode.of(getIndustryGroupCode());
}
/**
* Obtains a code object for the industry only.
* <p>
* This returns a {@code GICSCode} representing just the industry.
* Null is returned if this object represents a sector or industry-group.
*
* @return the object representing the industry, null if no industry code
*/
public GICSCode toIndustry() {
if (getCode().length() < 6) {
return null;
}
return GICSCode.of(getIndustryCode());
}
/**
* Obtains a code object for the sub-industry only.
* <p>
* This returns a {@code GICSCode} representing just the sub-industry.
* Null is returned if this object represents a sector, industry-group or industry.
*
* @return the object representing the industry, null if no industry code
*/
public GICSCode toSubIndustry() {
if (getCode().length() < 8) {
return null;
}
return this;
}
//-------------------------------------------------------------------------
/**
* Compares this code to another based on the combined code.
*
* @param obj the other code, null returns false
* @return true of equal
*/
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof GICSCode) {
GICSCode other = (GICSCode) obj;
return getCode().equals(other.getCode());
}
return false;
}
@Override
public int hashCode() {
return getCode().hashCode();
}
/**
* Returns a string description of the code, which includes the code and a description.
*
* @return the string version of the code, not null
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getCode());
sb.append(" ");
sb.append(getSubIndustryDescription());
return sb.toString();
}
}