blob: a7886fc07113bb332583a5583acf167d4e1d639e [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sysds.runtime.meta;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.EnumUtils;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.sysds.api.DMLScript;
import org.apache.sysds.common.Types;
import org.apache.sysds.parser.BooleanIdentifier;
import org.apache.sysds.parser.ConstIdentifier;
import org.apache.sysds.parser.DataExpression;
import org.apache.sysds.parser.DataIdentifier;
import org.apache.sysds.parser.DoubleIdentifier;
import org.apache.sysds.parser.Expression;
import org.apache.sysds.parser.LanguageException;
import org.apache.sysds.parser.ParseException;
import org.apache.sysds.parser.StringIdentifier;
import org.apache.sysds.runtime.DMLRuntimeException;
import org.apache.sysds.runtime.io.IOUtilFunctions;
import org.apache.sysds.runtime.util.HDFSTool;
import org.apache.sysds.utils.JSONHelper;
import org.apache.wink.json4j.JSONArray;
import org.apache.wink.json4j.JSONException;
import org.apache.wink.json4j.JSONObject;
public class MetaDataAll extends DataIdentifier {
// private static final Log LOG = LogFactory.getLog(MetaDataAll.class.getName());
private JSONObject _metaObj;
protected String _formatTypeString;
protected String _fineGrainedPrivacy;
protected String _schema;
protected String _delim = DataExpression.DEFAULT_DELIM_DELIMITER;
protected boolean _hasHeader = false;
protected boolean _sparseDelim = DataExpression.DEFAULT_DELIM_SPARSE;
public MetaDataAll() {
// do nothing
}
public MetaDataAll(String meta) {
try {
_metaObj = new JSONObject(meta);
}
catch(JSONException e) {
e.printStackTrace();
}
parseMetaDataParams();
}
public MetaDataAll(BufferedReader br) {
try {
_metaObj = JSONHelper.parse(br);
}
catch(Exception e) {
throw new DMLRuntimeException(e);
}
parseMetaDataParams();
}
public MetaDataAll(String mtdFileName, boolean conditional, boolean parseMeta) {
setFilename(mtdFileName);
_metaObj = readMetadataFile(mtdFileName, conditional);
if(parseMeta)
parseMetaDataParams();
}
public JSONObject readMetadataFile(String filename, boolean conditional)
{
JSONObject retVal = new JSONObject();
boolean exists = HDFSTool.existsFileOnHDFS(filename);
boolean isDir = exists ? HDFSTool.isDirectory(filename) : false;
// CASE: filename is a directory -- process as a directory
if( exists && isDir )
{
for(FileStatus stat : HDFSTool.getDirectoryListing(filename)) {
Path childPath = stat.getPath(); // gives directory name
if( !childPath.getName().startsWith("part") )
continue;
try (BufferedReader br = new BufferedReader(new InputStreamReader(
IOUtilFunctions.getFileSystem(childPath).open(childPath))))
{
JSONObject childObj = JSONHelper.parse(br);
for( Object obj : childObj.entrySet() ){
@SuppressWarnings("unchecked") Map.Entry<Object,Object> e = (Map.Entry<Object, Object>) obj;
Object key = e.getKey();
Object val = e.getValue();
retVal.put(key, val);
}
}
catch( IOException e){
raiseValidateError("for MTD file in directory, error parting part of MTD file with path " + childPath.toString() + ": " + e.getMessage(), conditional);
}
}
}
// CASE: filename points to a file
else if (exists) {
Path path = new Path(filename);
try (BufferedReader br = new BufferedReader(new InputStreamReader(
IOUtilFunctions.getFileSystem(path).open(path))))
{
retVal = new JSONObject(br);
}
catch (Exception e){
raiseValidateError("error parsing MTD file with path " + filename + ": " + e.getMessage(), conditional);
}
}
return retVal;
}
@SuppressWarnings("unchecked")
private void parseMetaDataParams()
{
for( Object obj : _metaObj.entrySet() ){
Map.Entry<Object,Object> e = (Map.Entry<Object, Object>) obj;
Object key = e.getKey();
Object val = e.getValue();
boolean isValidName = DataExpression.READ_VALID_MTD_PARAM_NAMES.contains(key);
if (!isValidName){ //wrong parameters always rejected
raiseValidateError("MTD file contains invalid parameter name: " + key, false);
}
parseMetaDataParam(key, val);
}
if(_format == null)
setFormatTypeString(null);
}
private void parseMetaDataParam(Object key, Object val)
{
switch(key.toString()) {
case DataExpression.READROWPARAM: _dim1 = val instanceof Long ? (Long) val : (Integer) val; break;
case DataExpression.READCOLPARAM: _dim2 = val instanceof Long ? (Long) val : (Integer) val; break;
case DataExpression.ROWBLOCKCOUNTPARAM: setBlocksize((Integer) val); break;
case DataExpression.READNNZPARAM: setNnz(val instanceof Long ? (Long) val : (Integer) val); break;
case DataExpression.FORMAT_TYPE: setFormatTypeString((String) val); break;
case DataExpression.DATATYPEPARAM: setDataType(Types.DataType.valueOf(((String) val).toUpperCase())); break;
case DataExpression.VALUETYPEPARAM: setValueType(Types.ValueType.fromExternalString((String) val)); break;
case DataExpression.DELIM_DELIMITER: setDelim(val.toString()); break;
case DataExpression.SCHEMAPARAM: setSchema(val.toString()); break;
case DataExpression.DELIM_HAS_HEADER_ROW:
if(val instanceof Boolean){
boolean valB = (Boolean) val;
setHasHeader(valB);
break;
}
else
setHasHeader(false);
break;
case DataExpression.DELIM_SPARSE: setSparseDelim((boolean) val);
}
}
public boolean mtdExists() {
return _metaObj != null && !_metaObj.isEmpty();
}
public String getFormatTypeString() {
return _formatTypeString;
}
public String getFineGrainedPrivacy() {
return _fineGrainedPrivacy;
}
public String getDelim() {
return _delim;
}
public String getSchema() {
return _schema;
}
public boolean getHasHeader() {
return _hasHeader;
}
public boolean getSparseDelim() {
return _sparseDelim;
}
public void setSparseDelim(boolean sparseDelim) {
_sparseDelim = sparseDelim;
}
public void setHasHeader(boolean hasHeader) {
_hasHeader = hasHeader;
}
public void setFineGrainedPrivacy(String fineGrainedPrivacy) {
_fineGrainedPrivacy = fineGrainedPrivacy;
}
public void setSchema(String schema) {
_schema = schema;
}
public void setDelim(String delim) {
if(delim.length() == 0)
throw new RuntimeException("Invalid metadata delim, cannot be empty string");
_delim = delim;
}
public void setFormatTypeString(String format) {
_formatTypeString = _formatTypeString != null && format == null && _metaObj != null ? (String)JSONHelper.get(_metaObj, DataExpression.FORMAT_TYPE) : format ;
if(_formatTypeString != null && EnumUtils.isValidEnum(Types.FileFormat.class, _formatTypeString.toUpperCase()))
setFileFormat(Types.FileFormat.safeValueOf(_formatTypeString));
}
public DataCharacteristics getDataCharacteristics() {
return new MatrixCharacteristics(getDim1(), getDim2(), getBlocksize(), getNnz());
}
@SuppressWarnings("unchecked")
public HashMap<String, Expression> parseMetaDataFileParameters(String mtdFileName, boolean conditional, HashMap<String, Expression> varParams)
{
for( Object obj : _metaObj.entrySet() ){
Map.Entry<Object,Object> e = (Map.Entry<Object, Object>) obj;
Object key = e.getKey();
Object val = e.getValue();
boolean isValidName = DataExpression.READ_VALID_MTD_PARAM_NAMES.contains(key);
if (!isValidName){ //wrong parameters always rejected
raiseValidateError("MTD file " + mtdFileName + " contains invalid parameter name: " + key, false);
}
parseMetaDataParam(key, val);
// if the read method parameter is a constant, then verify value matches MTD metadata file
if (varParams.get(key.toString()) != null && (varParams.get(key.toString()) instanceof ConstIdentifier)
&& !varParams.get(key.toString()).toString().equalsIgnoreCase(val.toString())) {
raiseValidateError("Parameter '" + key.toString()
+ "' has conflicting values in metadata and read statement. MTD file value: '"
+ val.toString() + "'. Read statement value: '" + varParams.get(key.toString()) + "'.", conditional);
} else {
// if the read method does not specify parameter value, then add MTD metadata file value to parameter list
if (varParams.get(key.toString()) == null){
if (( !key.toString().equalsIgnoreCase(DataExpression.DESCRIPTIONPARAM) ) &&
( !key.toString().equalsIgnoreCase(DataExpression.AUTHORPARAM) ) &&
( !key.toString().equalsIgnoreCase(DataExpression.CREATEDPARAM) ) )
{
StringIdentifier strId = new StringIdentifier(val.toString(), this);
if ( key.toString().equalsIgnoreCase(DataExpression.DELIM_HAS_HEADER_ROW)
|| key.toString().equalsIgnoreCase(DataExpression.DELIM_FILL)
|| key.toString().equalsIgnoreCase(DataExpression.DELIM_SPARSE)
) {
// parse these parameters as boolean values
BooleanIdentifier boolId = null;
if (strId.toString().equalsIgnoreCase("true")) {
boolId = new BooleanIdentifier(true, this);
} else if (strId.toString().equalsIgnoreCase("false")) {
boolId = new BooleanIdentifier(false, this);
} else {
raiseValidateError("Invalid value provided for '" + DataExpression.DELIM_HAS_HEADER_ROW + "' in metadata file '" + mtdFileName + "'. "
+ "Must be either TRUE or FALSE.", conditional);
}
varParams.remove(key.toString());
addVarParam(key.toString(), boolId, varParams);
switch(key.toString().toUpperCase()) {
case DataExpression.DELIM_HAS_HEADER_ROW:
case DataExpression.DELIM_FILL:
case DataExpression.DELIM_SPARSE:
}
}
else if ( key.toString().equalsIgnoreCase(DataExpression.DELIM_FILL_VALUE)) {
// parse these parameters as numeric values
DoubleIdentifier doubleId = new DoubleIdentifier(Double.parseDouble(strId.toString()),
this);
varParams.remove(key.toString());
addVarParam(key.toString(), doubleId, varParams);
}
else if (key.toString().equalsIgnoreCase(DataExpression.DELIM_NA_STRINGS)
|| key.toString().equalsIgnoreCase(DataExpression.PRIVACY)
|| key.toString().equalsIgnoreCase(DataExpression.FINE_GRAINED_PRIVACY)) {
String naStrings = null;
if ( val instanceof String) {
naStrings = val.toString();
}
else if (val instanceof JSONArray) {
StringBuilder sb = new StringBuilder();
JSONArray valarr = (JSONArray)val;
for(int naid=0; naid < valarr.size(); naid++ ) {
sb.append( (String) valarr.get(naid) );
if ( naid < valarr.size()-1)
sb.append( DataExpression.DELIM_NA_STRING_SEP );
}
naStrings = sb.toString();
}
else if ( val instanceof JSONObject ){
JSONObject valJsonObject = (JSONObject)val;
naStrings = valJsonObject.toString();
}
else {
throw new ParseException("Type of value " + val
+ " from metadata not recognized by parser.");
}
StringIdentifier sid = new StringIdentifier(naStrings, this);
varParams.remove(key.toString());
addVarParam(key.toString(), sid, varParams);
}
else {
// by default, treat a parameter as a string
addVarParam(key.toString(), strId, varParams);
}
}
}
}
if(_format == null)
setFormatTypeString(null);
}
return varParams;
}
public void addVarParam(String name, Expression value, HashMap<String, Expression> varParams) {
if (DMLScript.VALIDATOR_IGNORE_ISSUES && (value == null)) {
return;
}
varParams.put(name, value);
// if required, initialize values
setFilename(value.getFilename());
if (getBeginLine() == 0) setBeginLine(value.getBeginLine());
if (getBeginColumn() == 0) setBeginColumn(value.getBeginColumn());
if (getEndLine() == 0) setEndLine(value.getEndLine());
if (getEndColumn() == 0) setEndColumn(value.getEndColumn());
if (getText() == null) setText(value.getText());
}
public static String checkHasDelimitedFormat(String filename, boolean conditional) {
// if the MTD file exists, check the format is not binary
MetaDataAll mtdObject = new MetaDataAll(filename + ".mtd", conditional, false);
if (mtdObject.mtdExists()) {
try {
mtdObject.setFormatTypeString((String) mtdObject._metaObj.get(DataExpression.FORMAT_TYPE));
if(Types.FileFormat.isDelimitedFormat(mtdObject.getFormatTypeString()))
return mtdObject.getFormatTypeString();
}
catch(JSONException e) {
e.printStackTrace();
}
}
return null;
}
public static boolean checkHasMatrixMarketFormat(String inputFileName, String mtdFileName, boolean conditional)
{
// Check the MTD file exists. if there is an MTD file, return false.
MetaDataAll mtdObject = new MetaDataAll(mtdFileName, conditional, false);
if (mtdObject.mtdExists())
return false;
if( HDFSTool.existsFileOnHDFS(inputFileName)
&& !HDFSTool.isDirectory(inputFileName) )
{
Path path = new Path(inputFileName);
try( BufferedReader in = new BufferedReader(new InputStreamReader(
IOUtilFunctions.getFileSystem(path).open(path))))
{
String headerLine = new String("");
if (in.ready())
headerLine = in.readLine();
return (headerLine !=null && headerLine.startsWith("%%"));
}
catch(Exception ex) {
throw new LanguageException("Failed to read matrix market header.", ex);
}
}
return false;
}
@Override
public String toString() {
return "MetaDataAll\n" + _metaObj + "\n" + super.toString();
}
}