/** End element */
  @Override
  public void endElement(String uri, String localName, String qName) throws SAXException {

    str = sw.toString();

    String fullUri = uri != "" ? uri + "#" + localName : localName;

    if (fullUri.equalsIgnoreCase(nsOws + "#ServiceIdentification")) {
      capabilities.setServiceIdentification(serviceIdentification);
      inServiceIdentification = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#OperationsMetadata")) {
      capabilities.setOperationsMetadata(operationsMetadata);
      inOperationsMetadata = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Operation")) {
      if (inOperationsMetadata) {
        operationsMetadata.addOperation(operation);
      }
      inOperation = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Operation")) {
      if (inOperationsMetadata) {
        operationsMetadata.addOperation(operation);
      }
      inOperation = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Parameter")) {
      if (inOperation) {
        operation.addParameter(parameter);
      }
      inParameter = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Value")) {
      if (inParameter) {
        parameter.addAllowedValue(new String(str));
      }
    } else if (fullUri.equalsIgnoreCase(nsSos + "#ObservationOffering")) {
      // we loaded the offering object from the Capabilities document
      offering.loaded();
      // add offering to capabilities document
      // NOTE: right now we require offerings to have LAT LONG
      if (!noLatLong) capabilities.addSensorOffering(offering);
      inOffering = false;
    } else if (fullUri.equalsIgnoreCase(nsGml + "#description")) {
      offering.setDescription(str);
    } else if (fullUri.equalsIgnoreCase(nsGml + "#name")) {
      if (inOffering) offering.setName(str);
    } else if (fullUri.equalsIgnoreCase(nsGml + "#srsName")) {
      if (inOffering) offering.setSrsName(str);
    } else if (fullUri.equalsIgnoreCase(nsSos + "#responseFormat")) {
      if (inOffering) offering.addResponseFormat(str);
    } else if (fullUri.equalsIgnoreCase(nsSos + "#resultModel")) {
      if (inOffering) offering.setResultModel(str);
    } else if (fullUri.equalsIgnoreCase(nsSos + "#responseMode")) {
      if (inOffering) offering.setResponseMode(str);
    } else if (fullUri.equalsIgnoreCase(nsGml + "#lowerCorner")) {
      if (inOffering) {
        double latlong[] = getLatLong(str.trim());
        if (latlong != null) {
          offering.setLowerCornerLat(latlong[0]);
          offering.setLowerCornerLong(latlong[1]);
        } else {
          noLatLong = true;
        }
      }
    } else if (fullUri.equalsIgnoreCase(nsGml + "#upperCorner")) {
      if (inOffering) {
        double latlong[] = getLatLong(str.trim());
        if (latlong != null) {
          offering.setUpperCornerLat(latlong[0]);
          offering.setUpperCornerLong(latlong[1]);
        } else {
          noLatLong = true;
        }
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Title")) {
      if (inServiceIdentification) {
        serviceIdentification.setTitle(Util.cleanUpString(str));
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Abstract")) {
      if (inServiceIdentification) {
        serviceIdentification.setAbstract(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Keyword")) {
      if (inServiceIdentification) {
        serviceIdentification.addKeywords(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ServiceType")) {
      if (inServiceIdentification) {
        serviceIdentification.setServiceType(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ServiceTypeVersion")) {
      if (inServiceIdentification) {
        serviceIdentification.addServiceTypeVersion(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#AccessConstraints")) {
      if (inServiceIdentification) {
        serviceIdentification.addAccessConstraint(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Fees")) {
      if (inServiceIdentification) {
        serviceIdentification.setFees(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ServiceProvider")) {
      serviceProvider.setContact(serviceContact);
      capabilities.setServiceProvider(serviceProvider);
      inServiceProvider = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ProviderName")) {
      if (inServiceProvider) {
        serviceProvider.setName(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ServiceContact")) {
      if (inServiceContact) {
        String name = "";
        if (individualName != null) name = individualName;
        serviceContact = new ServiceContact(name, contactInfo);
      }
      inServiceContact = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#IndividualName")) {
      if (inServiceContact) individualName = new String(str);
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ContactInfo")) {
      if (phone != null) contactInfo.setPhone(phone);
      contactInfo.setAddress(address);
      inContactInfo = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Voice")) {
      if (inContactInfo) phone = new String(str);
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Address")) {
      inAddress = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#DeliveryPoint")) {
      if (inAddress) {
        address.setDeliveryPoint(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#City")) {
      if (inAddress) {
        address.setCity(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#AdministrativeArea")) {
      if (inAddress) {
        address.setAdministrativeArea(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#PostalCode")) {
      if (inAddress) {
        address.setPostalCode(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Country")) {
      if (inAddress) {
        address.setCountry(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ElectronicMailAddress")) {
      if (inAddress) {
        address.setEmaill(str);
      }
    } else if (fullUri.equalsIgnoreCase(nsSos + "#time")) {
      inTime = false;
    } else if (fullUri.equalsIgnoreCase(nsGml + "#beginPosition")) {
      if (inTime) {
        Date begin = TimeUtils.parseDefault(str);
        offering.setStartTime(begin);
      }
    } else if (fullUri.equalsIgnoreCase(nsGml + "#endPosition")) {
      if (inTime) {
        /*
         * Only parse the end time if it is given
         */
        if (endTimeIndeterminate) {
          endTimeIndeterminate = false;
        } else {
          Date end = TimeUtils.parseDefault(str);
          offering.setEndTime(end);
        }
      }
    } else if (fullUri.equalsIgnoreCase(nsGml + "#timeInterval")) {
      if (inTime) {
        // TODO: find out which units are valid
        double interval = 60.0 * Double.parseDouble(str);
        offering.setInterval(interval);
      }
    }
  }
  /** Start element */
  @Override
  public void startElement(String uri, String localName, String qName, Attributes attributes)
      throws SAXException {

    sw = new StringWriter();

    String fullUri = uri != "" ? uri + "#" + localName : localName;

    if (fullUri.equalsIgnoreCase(nsSos + "#Capabilities")) {
      capabilities = new SosCapabilities();
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ServiceIdentification")) {
      serviceIdentification = new ServiceIdentification();
      inServiceIdentification = true;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ServiceProvider")) {
      serviceProvider = new ServiceProvider();
      inServiceProvider = true;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ServiceContact")) {
      inServiceContact = true;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ContactInfo")) {
      contactInfo = new ContactInfo();
      inContactInfo = true;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#OperationsMetadata")) {
      operationsMetadata = new OperationsMetadata();
      inOperationsMetadata = true;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Operation")) {
      String name = attributes.getValue("name");
      operation = new Operation(name);
      inOperation = true;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Get")) {
      if (inOperation) {
        String address = attributes.getValue(nsXlink, "href");
        operation.addServiceAddress("get", address);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Post")) {
      if (inOperation) {
        String address = attributes.getValue(nsXlink, "href");
        operation.addServiceAddress("post", address);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Parameter")) {
      String name = attributes.getValue("name");
      parameter = new Parameter(name);
      inParameter = true;
    } else if (fullUri.equalsIgnoreCase(nsSos + "#ObservationOffering")) {
      String gmlId = attributes.getValue(nsGml, "id");
      offering = new SensorOffering(gmlId);
      inOffering = true;
      noLatLong = false;
    } else if (fullUri.equalsIgnoreCase(nsOws + "#ProviderSite")) {
      if (inServiceProvider) {
        String site = attributes.getValue(nsXlink, "href");
        serviceProvider.setSite(site);
      }
    } else if (fullUri.equalsIgnoreCase(nsOws + "#Address")) {
      if (inContactInfo) {
        address = new Address();
        inAddress = true;
      }
    } else if (fullUri.equalsIgnoreCase(nsSos + "#procedure")) {
      String procedure = attributes.getValue(nsXlink, "href");
      offering.addProcedure(procedure);
    } else if (fullUri.equalsIgnoreCase(nsSos + "#observedProperty")) {
      String property = attributes.getValue(nsXlink, "href");
      offering.addObservedProperty(property);
    } else if (fullUri.equalsIgnoreCase(nsSos + "#featureOfInterest")) {
      String feature = attributes.getValue(nsXlink, "href");
      offering.addFeatureOfInterest(feature);
    } else if (fullUri.equalsIgnoreCase(nsSos + "#time")) {
      if (inOffering) inTime = true;
    } else if (fullUri.equalsIgnoreCase(nsGml + "#endPosition")) {
      if (inTime) {
        String value = attributes.getValue("indeterminatePosition");
        if (value != null) endTimeIndeterminate = true;
      }
    } else if (fullUri.equalsIgnoreCase(nsGml + "#timeInterval")) {
      if (inTime) {
        timeUnits = attributes.getValue("unit");
      }
    }
  }