File:  [parser3project] / parser3 / src / types / pa_value.C
Revision 1.53: download - view: text, annotated - select for diffs - revision graph
Sat Apr 25 13:38:46 2026 UTC (6 weeks, 3 days ago) by moko
Branches: MAIN
CVS tags: HEAD
Copyright year updated, websites links changed to https://

/** @file
	Parser: Value class.

	Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com)
	Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
*/

#include "pa_value.h"
#include "pa_vstateless_class.h"
#include "pa_vmethod_frame.h"
#include "pa_vdate.h"
#include "pa_vobject.h"
#include "pa_request.h"


volatile const char * IDENT_PA_VALUE_C="$Id: pa_value.C,v 1.53 2026/04/25 13:38:46 moko Exp $" IDENT_PA_VALUE_H IDENT_PA_PROPERTY_H;

// globals

const String name_name(NAME_NAME);

const String value_name(VALUE_NAME);
const String expires_name(EXPIRES_NAME);
const String content_type_name(CONTENT_TYPE_NAME);

// methods

Junction* Value::get_junction() { return 0; }

Value* Value::get_element(const String& /*aname*/) {
	return bark("element cannot be fetched from %s");
}

VFile* Value::as_vfile() {
	throw Exception(PARSER_RUNTIME, 0, "parameter is '%s', it does not have file value", type());
}

// Should be synced with MethodParams::as_hash
HashStringValue* Value::as_hash(const char* name) {
	if(get_junction())
		throw Exception(PARSER_RUNTIME, 0, "%s must not be code", name ? name : "options");
	if(!is_defined()) // empty hash is not defined, but we don't need it anyway
		return 0;
	if(HashStringValue* result=get_hash())
		return result;
	if(is_string() && get_string()->trim().is_empty())
		return 0;
	throw Exception(PARSER_RUNTIME, 0, "%s must have hash representation", name ? name : "options");
}

const String* Value::get_json_string(Json_options& options) {
	if(HashStringValue* hash=get_hash())
		return options.hash_json_string(hash);

	if(!options.skip_unknown)
		throw Exception(PARSER_RUNTIME, 0, "Unsupported value's type (%s)", type());

	return new String("null");
}

const String* Value::default_method_2_json_string(Value& default_method, Json_options& options){
	if(default_method.is_string()){
		// specified as string with method name
		const String& method_name=*default_method.get_string();
		Method* method=this->get_class()->get_method(method_name);
		if(!method) {
			// class/object does not have method with specified name so serialize it as hash (default)
			return options.hash_json_string(get_hash());
		}

		Value *params[]={new VString(*new String(options.key, String::L_JSON)), options.params ? options.params : VVoid::get()};

		METHOD_FRAME_ACTION(*method, options.r->method_frame, *this,{
			frame.store_params(params, 2);
			options.r->call(frame);
			return &frame.result().as_string();
		});
	} else {
		// specified as method-junction
		Junction* junction=default_method.get_junction();

		Value *params[]={new VString(*new String(options.key, String::L_JSON)), this, options.params ? options.params : VVoid::get()};

		METHOD_FRAME_ACTION(*junction->method, options.r->method_frame, junction->self,{
			frame.store_params(params, 3);
			options.r->call(frame);
			return &frame.result().as_string();
		});
	}
}

/// call this before invoking to ensure proper actual numbered params count
void Method::check_actual_numbered_params(Value& self, MethodParams* actual_numbered_params) const {
	int actual_count=actual_numbered_params ? actual_numbered_params->count() : 0;
	if(actual_count<min_numbered_params_count || actual_count>max_numbered_params_count)
		throw Exception(PARSER_RUNTIME, name, "native method of '%s' accepts %s %d parameter(s) (%d present)", 
			self.type(),
			actual_count<min_numbered_params_count ? "minimum" : "maximum",
			actual_count<min_numbered_params_count ? min_numbered_params_count : max_numbered_params_count,
			actual_count);
}

// attributed meaning

static void append_attribute_meaning(String& result, Value& value, String::Language lang, bool forced) {
	if(const String* string=value.get_string())
		result.append(*string, lang, forced);
	else
		if(VDate* vdate=dynamic_cast<VDate*>(&value)) {
			result << *vdate->get_gmt_string();
		} else
			throw Exception(PARSER_RUNTIME, &result, "trying to append here neither string nor date (%s)", value.type());
}

#ifndef DOXYGEN
struct Attributed_meaning_info {
	String* header;        // header line being constructed
	String::Language lang; // language in which to append to that line
	bool forced;           // do they force that lang?
	bool allow_bool;       // allow bool types during print attributes
};
#endif

static void append_attribute_subattribute(HashStringValue::key_type akey, HashStringValue::value_type avalue, Attributed_meaning_info *info) {
	if(akey==VALUE_NAME)
		return;

	if(avalue->is_bool() && (!info->allow_bool || avalue->as_bool()==false))
		return;

	// ...; charset=windows1251
	*info->header << "; ";
	info->header->append(String(akey, String::L_TAINTED), info->lang, info->forced);
	if(!avalue->is_bool()) {
		if( akey==content_disposition_filename_name ) {
			*info->header << "=\"";
			append_attribute_meaning(*info->header, *avalue, info->lang, info->forced);
			*info->header << "\"";
		} else {
			*info->header << "=";
			append_attribute_meaning(*info->header, *avalue, info->lang, info->forced);
		}
	}
}
const String& attributed_meaning_to_string(Value& meaning, String::Language lang, bool forced, bool allow_bool) {
	String& result=*new String;
	if(HashStringValue *hash=meaning.get_hash()) {
		// $value(value) $subattribute(subattribute value)
		if(Value* value=hash->get(value_name))
			append_attribute_meaning(result, *value, lang, forced);

		Attributed_meaning_info attributed_meaning_info={&result, lang, false, allow_bool};
		hash->for_each<Attributed_meaning_info*>(append_attribute_subattribute, &attributed_meaning_info);
	} else // result value
		append_attribute_meaning(result, meaning, lang, forced);

	return result;
}

E-mail: