File: HashTools.pm

package info (click to toggle)
tiarra 20100212-4
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 2,732 kB
  • ctags: 1,712
  • sloc: perl: 32,032; lisp: 193; sh: 109; makefile: 10
file content (161 lines) | stat: -rw-r--r-- 4,000 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# -*- cperl -*-
# -----------------------------------------------------------------------------
# $Id: HashTools.pm 11365 2008-05-10 14:58:28Z topia $
# -----------------------------------------------------------------------------
# copyright (C) 2003 Topia <topia@clovery.jp>. all rights reserved.

# ハッシュをフォーマットする関数群。

package Tools::HashTools;

sub get_value_random {
    my ($hash, $key) = @_;

    my $values = get_array($hash, $key);
    if ($values) {
	# 発見. どれか一つ選ぶ。
	my $idx = int(rand() * hex('0xffffffff')) % @$values;
	return $values->[$idx];
    }
    return undef;
}

sub get_value {
    my ($hash, $key) = @_;

    my $values = get_array($hash, $key);
    if ($values) {
	# 発見.
	return $values->[0];
    }
    return undef;
}

sub get_array {
    my ($hash, $key) = @_;

    my $value = $hash->{$key};
    if (defined $value) {
	# 発見
	if (ref($value) eq 'ARRAY') {
	    return $value;
	} else {
	    return [$value];
	}
	last;
    }
    return undef;
}

sub replace_recursive {
    # ()がネスト可能な_replace.

    # ていうか ad hoc 過ぎる気がするなあ。良い解析方法無いかな。

    my ($str,$hashtables,$callbacks) = @_;

    return '' if !defined($str) || ($str eq '');

    my $start = 0;
    my $end;
    my $pos;
    while (($pos = $start = index($str, '#(', $start)) != -1) {
	# 検索開始。
	my $level = 1;
	do {
	    # こっかを探す。
	    $end = index($str, ')', $pos + 1);
	    if ($end == -1) {
		# こっかが無い。困ったことになったが、終わった後にこっかがあったことにして誤魔化そう。
		$str .= ')';
		$end = length($str);
		last;
	    }

	    # かっこを探す。
	    my $next = index($str, '(', $pos + 2);
	    if ($next == -1 || $next > $end) {
		# かっこが無かったか、こっかより後。階層レベルを減らして検索位置を次のこっかに移す。
		$pos = $end;
		$level--;
	    } else {
		# こっかより前にかっこがあった。階層レベルを増やして繰り返す。
		$pos = $next;
		$level++;
	    }
	} while ($level > 0);	# 階層レベルが0になるまで繰り返し。
	# こっかの前までを抽出範囲とする。
	$end--;
	#proc $start  to  $end
	my $work = substr($str, $start + 2, $end - $start - 1);
	$work = _replace($work,$hashtables,$callbacks);
	substr($str, $start, $end - $start + 2) = $work;
	$start = $start + length($work);
    }

    return $str;
}

sub _replace {
    my ($str,$hashtables,$callbacks) = @_;

    # variables := variable ( '|' variable )*
    # variable  := key ( ';' format )?
    foreach my $variable (split /\|/,$str) {
	my ($key, $format) = split(/;/,$variable,2);
	my ($ret) = undef;
	if (defined($key) && $key ne '') {
	    foreach my $table (@$hashtables) {
		$ret =  get_value($table, $key);
		last if (defined $ret);
	    }
	    if (!defined $ret) {
		# not found.
		foreach my $callback (@$callbacks) {
		    if (defined $callback) {
			# callback function definition: func($key, [hashtables], [callbacks]);
			my $value = $callback->($key, $hashtables, $callbacks);
			if (defined $value) {
			    $ret = $value;
			    last;
			}
		    }
		}
	    }
	} else {
	    # callback等がエラーを吐くので強制的に''を入れる。
	    $ret = '';
	}
	if (defined $ret) {
	    if (defined $format) {
		return _format($format,$ret,$hashtables,$callbacks);
	    } else {
		return $ret;
	    }
	}
    }
    # 最終的に見付からなければ$strそのものを返す。
    return $str;
}

sub _format {
    # %s形式の値をフォーマットする。
    # replace_recursiveを呼び出して再帰変換も行う。
    my ($str,$value,$hashtables,$callbacks) = @_;

    $str = replace_recursive($str,$hashtables,$callbacks);
    $str =~ s/%(.)/_format_percent($1, $value)/eg;
    return $str;
}

sub _format_percent {
    $char = shift;

    if ($char eq 's') {
	return $_[0];
    } else {
	return $char;
    }
}

1;