aboutsummaryrefslogtreecommitdiffstats
path: root/lib/asn1/src/asn1ct_check.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/asn1/src/asn1ct_check.erl')
-rw-r--r--lib/asn1/src/asn1ct_check.erl7399
1 files changed, 7399 insertions, 0 deletions
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
new file mode 100644
index 0000000000..f0a48a086b
--- /dev/null
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -0,0 +1,7399 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(asn1ct_check).
+
+%% Main Module for ASN.1 compile time functions
+
+%-compile(export_all).
+-export([check/2,storeindb/2]).
+%-define(debug,1).
+-include("asn1_records.hrl").
+%%% The tag-number for universal types
+-define(N_BOOLEAN, 1).
+-define(N_INTEGER, 2).
+-define(N_BIT_STRING, 3).
+-define(N_OCTET_STRING, 4).
+-define(N_NULL, 5).
+-define(N_OBJECT_IDENTIFIER, 6).
+-define(N_OBJECT_DESCRIPTOR, 7).
+-define(N_EXTERNAL, 8). % constructed
+-define(N_INSTANCE_OF,8).
+-define(N_REAL, 9).
+-define(N_ENUMERATED, 10).
+-define(N_EMBEDDED_PDV, 11). % constructed
+-define(N_UTF8String, 12).
+-define('N_RELATIVE-OID',13).
+-define(N_SEQUENCE, 16).
+-define(N_SET, 17).
+-define(N_NumericString, 18).
+-define(N_PrintableString, 19).
+-define(N_TeletexString, 20).
+-define(N_VideotexString, 21).
+-define(N_IA5String, 22).
+-define(N_UTCTime, 23).
+-define(N_GeneralizedTime, 24).
+-define(N_GraphicString, 25).
+-define(N_VisibleString, 26).
+-define(N_GeneralString, 27).
+-define(N_UniversalString, 28).
+-define(N_CHARACTER_STRING, 29). % constructed
+-define(N_BMPString, 30).
+
+-define(TAG_PRIMITIVE(Num),
+ case S#state.erule of
+ ber_bin_v2 ->
+ #tag{class='UNIVERSAL',number=Num,type='IMPLICIT',form=0};
+ _ -> []
+ end).
+-define(TAG_CONSTRUCTED(Num),
+ case S#state.erule of
+ ber_bin_v2 ->
+ #tag{class='UNIVERSAL',number=Num,type='IMPLICIT',form=32};
+ _ -> []
+ end).
+
+-record(newt,{type=unchanged,tag=unchanged,constraint=unchanged,inlined=no}). % used in check_type to update type and tag
+-record(newv,{type=unchanged,value=unchanged}). % used in check_value to update type and value
+
+check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) ->
+ %%Predicates used to filter errors
+ TupleIs = fun({T,_},T) -> true;
+ (_,_) -> false
+ end,
+ IsClass = fun(X) -> TupleIs(X,asn1_class) end,
+ IsObjSet = fun(X) -> TupleIs(X,objectsetdef) end,
+ IsPObjSet = fun(X) -> TupleIs(X,pobjectsetdef) end,
+ IsObject = fun(X) -> TupleIs(X,objectdef) end,
+ IsValueSet = fun(X) -> TupleIs(X,valueset) end,
+ Element2 = fun(X) -> element(2,X) end,
+ Element1 = fun(X) -> element(1,X) end,
+
+ %% initialize internal book keeping
+ save_asn1db_uptodate(S,S#state.erule,S#state.mname),
+ put(top_module,S#state.mname),
+
+ _Perror = checkp(S,ParameterizedTypes,[]), % must do this before the templates are used
+
+ %% table to save instances of parameterized objects,object sets
+ asn1ct:create_ets_table(parameterized_objects,[named_table]),
+ asn1ct:create_ets_table(inlined_objects,[named_table]),
+
+
+ Terror = checkt(S,Types,[]),
+ ?dbg("checkt finished with errors:~n~p~n~n",[Terror]),
+
+ %% get parameterized object sets sent to checkt/3
+ %% and update Terror
+
+ {PObjSetNames1,Terror2} = filter_errors(IsPObjSet,Terror),
+
+ Verror = checkv(S,Values ++ ObjectSets,[]), %value sets may be parsed as object sets
+ ?dbg("checkv finished with errors:~n~p~n~n",[Verror]),
+ %% get information object classes wrongly sent to checkt/3
+ %% and update Terror2
+
+ {AddClasses,Terror3} = filter_errors(IsClass,Terror2),
+
+ NewClasses = Classes++AddClasses,
+
+ Cerror = checkc(S,NewClasses,[]),
+ ?dbg("checkc finished with errors:~n~p~n~n",[Cerror]),
+ %% get object sets incorrectly sent to checkv/3
+ %% and update Verror
+
+ {ObjSetNames,Verror2} = filter_errors(IsObjSet,Verror),
+
+ %% get parameterized object sets incorrectly sent to checkv/3
+ %% and update Verror2
+
+ {PObjSetNames,Verror3} = filter_errors(IsPObjSet,Verror2),
+
+ %% get objects incorrectly sent to checkv/3
+ %% and update Verror3
+
+ {ObjectNames,Verror4} = filter_errors(IsObject,Verror3),
+
+ NewObjects = Objects++ObjectNames,
+ NewObjectSets = ObjSetNames ++ PObjSetNames ++ PObjSetNames1,
+
+ %% get value sets
+ %% and update Verror4
+
+ {ValueSetNames,Verror5} = filter_errors(IsValueSet,Verror4),
+
+ {Oerror,ExclO,ExclOS} = checko(S,NewObjects ++
+ NewObjectSets,
+ [],[],[]),
+ ?dbg("checko finished with errors:~n~p~n~n",[Oerror]),
+ InlinedObjTuples = ets:tab2list(inlined_objects),
+ InlinedObjects = lists:map(Element2,InlinedObjTuples),
+ ets:delete(inlined_objects),
+ ParameterizedElems = ets:tab2list(parameterized_objects),
+ ParObjectSets = lists:filter(fun({_OSName,objectset,_}) -> true;
+ (_)-> false end,ParameterizedElems),
+ ParObjectSetNames = lists:map(Element1,ParObjectSets),
+ ParTypes = lists:filter(fun({_TypeName,type,_}) -> true;
+ (_) -> false end, ParameterizedElems),
+ ParTypesNames = lists:map(Element1,ParTypes),
+ ets:delete(parameterized_objects),
+ put(asn1_reference,undefined),
+
+ Exporterror = check_exports(S,S#state.module),
+ ImportError = check_imports(S,S#state.module),
+
+ case {Terror3,Verror5,Cerror,Oerror,Exporterror,ImportError} of
+ {[],[],[],[],[],[]} ->
+ ContextSwitchTs = context_switch_in_spec(),
+ InstanceOf = instance_of_in_spec(S#state.mname),
+ NewTypes = lists:subtract(Types,AddClasses) ++ ContextSwitchTs
+ ++ InstanceOf ++ ParTypesNames,
+ NewValues = lists:subtract(Values,PObjSetNames++ObjectNames++
+ ValueSetNames),
+ {ok,
+ {NewTypes,NewValues,ParameterizedTypes,
+ NewClasses,NewObjects,NewObjectSets},
+ {NewTypes,NewValues,ParameterizedTypes,NewClasses,
+ lists:subtract(NewObjects,ExclO)++InlinedObjects,
+ lists:subtract(NewObjectSets,ExclOS)++ParObjectSetNames}};
+ _ ->{error,{asn1,lists:flatten([Terror3,Verror5,Cerror,
+ Oerror,Exporterror,ImportError])}}
+ end.
+
+context_switch_in_spec() ->
+ L = [{external,'EXTERNAL'},
+ {embedded_pdv,'EMBEDDED PDV'},
+ {character_string,'CHARACTER STRING'}],
+ F = fun({T,TName},Acc) ->
+ case get(T) of
+ generate -> erase(T),
+ [TName|Acc];
+ _ -> Acc
+ end
+ end,
+ lists:foldl(F,[],L).
+
+instance_of_in_spec(ModName) ->
+ case get(instance_of) of
+ L when is_list(L) ->
+ case lists:member(ModName,L) of
+ true ->
+ erase(instance_of),
+ ['INSTANCE OF'];
+ _ ->
+ erase(instance_of),
+ []
+ end;
+ _ ->
+ []
+ end.
+instance_of_decl(ModName) ->
+ Mods = get_instance_of(),
+ case lists:member(ModName,Mods) of
+ true ->
+ ok;
+ _ ->
+ put(instance_of,[ModName|Mods])
+ end.
+get_instance_of() ->
+ case get(instance_of) of
+ undefined ->
+ [];
+ L ->
+ L
+ end.
+
+put_once(T,State) ->
+ %% state is one of undefined, unchecked, generate
+ %% undefined > unchecked > generate
+ case get(T) of
+ PrevS when PrevS > State ->
+ put(T,State);
+ _ ->
+ ok
+ end.
+
+filter_errors(Pred,ErrorList) ->
+ Element2 = fun(X) -> element(2,X) end,
+ RemovedTupleElements = lists:filter(Pred,ErrorList),
+ RemovedNames = lists:map(Element2,RemovedTupleElements),
+ %% remove value set name tuples from Verror
+ RestErrors = lists:subtract(ErrorList,RemovedTupleElements),
+ {RemovedNames,RestErrors}.
+
+
+check_exports(S,Module = #module{}) ->
+ case Module#module.exports of
+ {exports,[]} ->
+ [];
+ {exports,all} ->
+ [];
+ {exports,ExportList} when is_list(ExportList) ->
+ IsNotDefined =
+ fun(X) ->
+ case catch get_referenced_type(S,X) of
+ {error,{asn1,_}} ->
+ true;
+ _ -> false
+ end
+ end,
+ case lists:filter(IsNotDefined,ExportList) of
+ [] ->
+ [];
+ NoDefExp ->
+ GetName =
+ fun(T = #'Externaltypereference'{type=N})->
+ %%{exported,undefined,entity,N}
+ NewS=S#state{type=T,tname=N},
+ error({export,"exported undefined entity",NewS})
+ end,
+ lists:map(GetName,NoDefExp)
+ end
+ end.
+
+check_imports(S,Module = #module{ }) ->
+ case Module#module.imports of
+ {imports,[]} ->
+ [];
+ {imports,ImportList} when is_list(ImportList) ->
+ check_imports2(S,ImportList,[]);
+ _ ->
+ []
+ end.
+check_imports2(_S,[],Acc) ->
+ Acc;
+check_imports2(S,[#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs],Acc) ->
+ NameOfDef =
+ fun(#'Externaltypereference'{type=N}) -> N;
+ (#'Externalvaluereference'{value=N}) -> N
+ end,
+ Module = NameOfDef(ModuleRef),
+ Refs = [{M,R}||{{M,_},R} <- [{catch get_referenced_type(S,Ref),Ref}||Ref <- Imports]],
+ {Illegal,Other} = lists:splitwith(fun({error,_}) -> true;(_) -> false end,
+ Refs),
+ ChainedRefs = [R||{M,R} <- Other, M =/= Module],
+ IllegalRefs = [R||{error,R} <- Illegal] ++
+ [R||{M,R} <- ChainedRefs,
+ ok =/= chained_import(S,Module,M,NameOfDef(R))],
+ ReportError =
+ fun(Ref) ->
+ NewS=S#state{type=Ref,tname=NameOfDef(Ref)},
+ error({import,"imported undefined entity",NewS})
+ end,
+ check_imports2(S,SFMs,[ReportError(Err)||Err <- IllegalRefs]++Acc).
+
+chained_import(S,ImpMod,DefMod,Name) ->
+ %% Name is a referenced structure that is not defined in ImpMod,
+ %% but must be present in the Imports list of ImpMod. The chain of
+ %% imports of Name must end in DefMod.
+ NameOfDef =
+ fun(#'Externaltypereference'{type=N}) -> N;
+ (#'Externalvaluereference'{value=N}) -> N;
+ (Other) -> Other
+ end,
+ GetImports =
+ fun(_M_) ->
+ case asn1_db:dbget(_M_,'MODULE') of
+ #module{imports={imports,ImportList}} ->
+ ImportList;
+ _ -> []
+ end
+ end,
+ FindNameInImports =
+ fun([],N,_) -> {no_mod,N};
+ ([#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs],N,F) ->
+ case [NameOfDef(X)||X <- Imports, NameOfDef(X) =:= N] of
+ [] -> F(SFMs,N,F);
+ [N] -> {NameOfDef(ModuleRef),N}
+ end
+ end,
+ case GetImports(ImpMod) of
+ [] ->
+ error;
+ Imps ->
+ case FindNameInImports(Imps,Name,FindNameInImports) of
+ {no_mod,_} ->
+ error;
+ {DefMod,_} -> ok;
+ {OtherMod,_} ->
+ chained_import(S,OtherMod,DefMod,Name)
+ end
+ end.
+
+checkt(S,[Name|T],Acc) ->
+ ?dbg("Checking type ~p~n",[Name]),
+ Result =
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({type,{internal_error,'???'},S});
+ Type when is_record(Type,typedef) ->
+ NewS = S#state{type=Type,tname=Name},
+ case catch(check_type(NewS,Type,Type#typedef.typespec)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1_class,_ClassDef} ->
+ {asn1_class,Name};
+ pobjectsetdef ->
+ {pobjectsetdef,Name};
+ pvalueset ->
+ {pvalueset,Name};
+ Ts ->
+ case Type#typedef.checked of
+ true -> % already checked and updated
+ ok;
+ _ ->
+ NewTypeDef = Type#typedef{checked=true,typespec = Ts},
+
+ asn1_db:dbput(NewS#state.mname,Name,NewTypeDef), % update the type
+
+ ok
+ end
+ end
+ end,
+ case Result of
+ ok ->
+ checkt(S,T,Acc);
+ _ ->
+ checkt(S,T,[Result|Acc])
+ end;
+checkt(S,[],Acc) ->
+ case check_contextswitchingtypes(S,[]) of
+ [] ->
+ lists:reverse(Acc);
+ L ->
+ checkt(S,L,Acc)
+ end.
+
+check_contextswitchingtypes(S,Acc) ->
+ CSTList=[{external,'EXTERNAL'},
+ {embedded_pdv,'EMBEDDED PDV'},
+ {character_string,'CHARACTER STRING'}],
+ check_contextswitchingtypes(S,CSTList,Acc).
+
+check_contextswitchingtypes(S,[{T,TName}|Ts],Acc) ->
+ case get(T) of
+ unchecked ->
+ put(T,generate),
+ check_contextswitchingtypes(S,Ts,[TName|Acc]);
+ _ ->
+ check_contextswitchingtypes(S,Ts,Acc)
+ end;
+check_contextswitchingtypes(_,[],Acc) ->
+ Acc.
+
+checkv(S,[Name|T],Acc) ->
+ ?dbg("Checking valuedef ~p~n",[Name]),
+ Result = case asn1_db:dbget(S#state.mname,Name) of
+ undefined -> error({value,{internal_error,'???'},S});
+ Value when is_record(Value,valuedef);
+ is_record(Value,typedef); %Value set may be parsed as object set.
+ is_record(Value,pvaluedef);
+ is_record(Value,pvaluesetdef) ->
+ NewS = S#state{value=Value},
+ case catch(check_value(NewS,Value)) of
+ {error,Reason} ->
+ error({value,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({value,{internal_error,Reason},NewS});
+ {pobjectsetdef} ->
+ {pobjectsetdef,Name};
+ {objectsetdef} ->
+ {objectsetdef,Name};
+ {objectdef} ->
+ %% this is an object, save as typedef
+ #valuedef{checked=C,pos=Pos,name=N,type=Type,
+ value=Def}=Value,
+ ClassName = Type#type.def,
+ NewSpec = #'Object'{classname=ClassName,
+ def=Def},
+ NewDef = #typedef{checked=C,pos=Pos,name=N,
+ typespec=NewSpec},
+ asn1_db:dbput(NewS#state.mname,Name,NewDef),
+ {objectdef,Name};
+ {valueset,VSet} ->
+ Pos = asn1ct:get_pos_of_def(Value),
+ CheckedVSDef = #typedef{checked=true,pos=Pos,
+ name=Name,typespec=VSet},
+ asn1_db:dbput(NewS#state.mname,Name,CheckedVSDef),
+ {valueset,Name};
+ V ->
+ %% update the valuedef
+ asn1_db:dbput(NewS#state.mname,Name,V),
+ ok
+ end
+ end,
+ case Result of
+ ok ->
+ checkv(S,T,Acc);
+ _ ->
+ checkv(S,T,[Result|Acc])
+ end;
+checkv(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+
+checkp(S,[Name|T],Acc) ->
+ %io:format("check_ptypedef:~p~n",[Name]),
+ Result = case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({type,{internal_error,'???'},S});
+ Type when is_record(Type,ptypedef) ->
+ NewS = S#state{type=Type,tname=Name},
+ case catch(check_ptype(NewS,Type,Type#ptypedef.typespec)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1_class,_ClassDef} ->
+ {asn1_class,Name};
+ {asn1_param_class,_} -> ok;
+ Ts ->
+ NewType = Type#ptypedef{checked=true,typespec = Ts},
+ asn1_db:dbput(NewS#state.mname,Name,NewType), % update the type
+ ok
+ end
+ end,
+ case Result of
+ ok ->
+ checkp(S,T,Acc);
+ _ ->
+ checkp(S,T,[Result|Acc])
+ end;
+checkp(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+
+
+
+checkc(S,[Name|Cs],Acc) ->
+ Result =
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({class,{internal_error,'???'},S});
+ Class ->
+ ClassSpec = if
+ is_record(Class,classdef) ->
+% Class#classdef.typespec;
+ Class;
+ is_record(Class,typedef) ->
+ Class#typedef.typespec
+ end,
+ NewS = S#state{type=Class,tname=Name},
+ case catch(check_class(NewS,ClassSpec)) of
+ {error,Reason} ->
+ error({class,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({class,{internal_error,Reason},NewS});
+ C ->
+ %% update the classdef
+ NewClass =
+ if
+ is_record(Class,classdef) ->
+ Class#classdef{checked=true,typespec=C};
+ is_record(Class,typedef) ->
+ #classdef{checked=true,name=Name,typespec=C}
+ end,
+ asn1_db:dbput(NewS#state.mname,Name,NewClass),
+ ok
+ end
+ end,
+ case Result of
+ ok ->
+ checkc(S,Cs,Acc);
+ _ ->
+ checkc(S,Cs,[Result|Acc])
+ end;
+checkc(_S,[],Acc) ->
+%% include_default_class(S#state.mname),
+ lists:reverse(Acc).
+
+checko(S,[Name|Os],Acc,ExclO,ExclOS) ->
+ ?dbg("Checking object ~p~n",[Name]),
+ Result =
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({type,{internal_error,'???'},S});
+ Object when is_record(Object,typedef) ->
+ NewS = S#state{type=Object,tname=Name},
+ case catch(check_object(NewS,Object,Object#typedef.typespec)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1,Reason} ->
+ error({type,Reason,NewS});
+ O ->
+ NewObj = Object#typedef{checked=true,typespec=O},
+ asn1_db:dbput(NewS#state.mname,Name,NewObj),
+ if
+ is_record(O,'Object') ->
+ case O#'Object'.gen of
+ true ->
+ {ok,ExclO,ExclOS};
+ false ->
+ {ok,[Name|ExclO],ExclOS}
+ end;
+ is_record(O,'ObjectSet') ->
+ case O#'ObjectSet'.gen of
+ true ->
+ {ok,ExclO,ExclOS};
+ false ->
+ {ok,ExclO,[Name|ExclOS]}
+ end
+ end
+ end;
+ PObject when is_record(PObject,pobjectdef) ->
+ NewS = S#state{type=PObject,tname=Name},
+ case (catch check_pobject(NewS,PObject)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1,Reason} ->
+ error({type,Reason,NewS});
+ PO ->
+ NewPObj = PObject#pobjectdef{def=PO},
+ asn1_db:dbput(NewS#state.mname,Name,NewPObj),
+ {ok,[Name|ExclO],ExclOS}
+ end;
+ PObjSet when is_record(PObjSet,pvaluesetdef) ->
+ %% this is a parameterized object set. Might be a parameterized
+ %% value set, couldn't it?
+ NewS = S#state{type=PObjSet,tname=Name},
+ case (catch check_pobjectset(NewS,PObjSet)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1,Reason} ->
+ error({type,Reason,NewS});
+ POS ->
+ %%NewPObjSet = PObjSet#pvaluesetdef{valueset=POS},
+ asn1_db:dbput(NewS#state.mname,Name,POS),
+ {ok,ExclO,[Name|ExclOS]}
+ end
+ end,
+ case Result of
+ {ok,NewExclO,NewExclOS} ->
+ checko(S,Os,Acc,NewExclO,NewExclOS);
+ _ ->
+ checko(S,Os,[Result|Acc],ExclO,ExclOS)
+ end;
+checko(_S,[],Acc,ExclO,ExclOS) ->
+ {lists:reverse(Acc),lists:reverse(ExclO),lists:reverse(ExclOS)}.
+
+check_class(S,CDef=#classdef{checked=Ch,name=Name,typespec=TS}) ->
+ case Ch of
+ true -> TS;
+ idle -> TS;
+ _ ->
+ store_class(S,idle,CDef,Name),
+ CheckedTS = check_class(S,TS),
+ store_class(S,true,CDef#classdef{typespec=CheckedTS},Name),
+ CheckedTS
+ end;
+check_class(S = #state{mname=M,tname=T},ClassSpec)
+ when is_record(ClassSpec,type) ->
+ Def = ClassSpec#type.def,
+ case Def of
+ #'Externaltypereference'{module=M,type=T} ->
+ #objectclass{fields=Def}; % in case of recursive definitions
+ Tref = #'Externaltypereference'{type=TName} ->
+ {MName,RefType} = get_referenced_type(S,Tref),
+ case is_class(S,RefType) of
+ true ->
+ NewState = update_state(S#state{type=RefType,
+ tname=TName},MName),
+ check_class(NewState,get_class_def(S,RefType));
+ _ ->
+ error({class,{internal_error,RefType},S})
+ end;
+ {pt,ClassRef,Params} ->
+ %% parameterized class
+ {_,PClassDef} = get_referenced_type(S,ClassRef),
+ NewParaList =
+ [match_parameters(S,TmpParam,S#state.parameters)||
+ TmpParam <- Params],
+ instantiate_pclass(S,PClassDef,NewParaList)
+ end;
+check_class(S,C) when is_record(C,objectclass) ->
+ NewFieldSpec = check_class_fields(S,C#objectclass.fields),
+ C#objectclass{fields=NewFieldSpec};
+check_class(_S,{poc,_ObjSet,_Params}) ->
+ 'fix this later';
+check_class(S,ClassName) ->
+ {RefMod,Def} = get_referenced_type(S,ClassName),
+ case Def of
+ ClassDef when is_record(ClassDef,classdef) ->
+ case ClassDef#classdef.checked of
+ true ->
+ ClassDef#classdef.typespec;
+ idle ->
+ ClassDef#classdef.typespec;
+ false ->
+ Name=ClassName#'Externaltypereference'.type,
+ store_class(S,idle,ClassDef,Name),
+% NewS = S#state{mname=RefMod,type=Def,tname=Name},
+ NewS = update_state(S#state{type=Def,tname=Name},RefMod),
+ CheckedTS = check_class(NewS,ClassDef#classdef.typespec),
+ store_class(S,true,ClassDef#classdef{typespec=CheckedTS},Name),
+ CheckedTS
+ end;
+ TypeDef when is_record(TypeDef,typedef) ->
+ %% this case may occur when a definition is a reference
+ %% to a class definition.
+ case TypeDef#typedef.typespec of
+ #type{def=Ext} when is_record(Ext,'Externaltypereference') ->
+ check_class(S,Ext)
+ end
+ end.
+
+instantiate_pclass(S=#state{parameters=_OldArgs},PClassDef,Params) ->
+ #ptypedef{args=Args,typespec=Type} = PClassDef,
+ MatchedArgs = match_args(S,Args, Params, []),
+% NewS = S#state{type=Type,parameters=MatchedArgs++OldArgs,abscomppath=[]},
+ NewS = S#state{type=Type,parameters=MatchedArgs,abscomppath=[]},
+ check_class(NewS,#classdef{name=S#state.tname,typespec=Type}).
+
+store_class(S,Mode,ClassDef,ClassName) ->
+ NewCDef = ClassDef#classdef{checked=Mode},
+ asn1_db:dbput(S#state.mname,ClassName,NewCDef).
+
+check_class_fields(S,Fields) ->
+ check_class_fields(S,Fields,[]).
+
+check_class_fields(S,[F|Fields],Acc) ->
+ NewField =
+ case element(1,F) of
+ fixedtypevaluefield ->
+ {_,Name,Type,Unique,OSpec} = F,
+ RefType = check_type(S,#typedef{typespec=Type},Type),
+ {fixedtypevaluefield,Name,RefType,Unique,OSpec};
+ object_or_fixedtypevalue_field ->
+ {_,Name,Type,Unique,OSpec} = F,
+ Type2 = maybe_unchecked_OCFT(S,Type),
+ Cat =
+ case asn1ct_gen:type(asn1ct_gen:get_inner(Type2#type.def)) of
+ Def when is_record(Def,typereference);
+ is_record(Def,'Externaltypereference') ->
+ {_,D} = get_referenced_type(S,Def),
+ D;
+ {undefined,user} ->
+ %% neither of {primitive,bif} or {constructed,bif}
+
+ {_,D} = get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=Type#type.def}),
+ D;
+ _ ->
+ Type
+ end,
+ case Cat of
+ Class when is_record(Class,classdef) ->
+ %% Type must be a referenced type => change it
+ %% to an external reference.
+ ToExt = fun(#type{def= CE = #'Externaltypereference'{}}) -> CE; (T) -> T end,
+ {objectfield,Name,ToExt(Type),Unique,OSpec};
+ _ ->
+ RefType = check_type(S,#typedef{typespec=Type},Type),
+ {fixedtypevaluefield,Name,RefType,Unique,OSpec}
+ end;
+ objectset_or_fixedtypevalueset_field ->
+ {_,Name,Type,OSpec} = F,
+ RefType =
+ case (catch check_type(S,#typedef{typespec=Type},Type)) of
+ {asn1_class,_ClassDef} ->
+ case if_current_checked_type(S,Type) of
+ true ->
+ Type#type.def;
+ _ ->
+ check_class(S,Type)
+ end;
+ CheckedType when is_record(CheckedType,type) ->
+ CheckedType;
+ _ ->
+ error({class,"internal error, check_class_fields",S})
+ end,
+ if
+ is_record(RefType,'Externaltypereference') ->
+ {objectsetfield,Name,Type,OSpec};
+ is_record(RefType,classdef) ->
+ {objectsetfield,Name,Type,OSpec};
+ is_record(RefType,objectclass) ->
+ {objectsetfield,Name,Type,OSpec};
+ true ->
+ {fixedtypevaluesetfield,Name,RefType,OSpec}
+ end;
+ typefield ->
+ case F of
+ {TF,Name,{'DEFAULT',Type}} ->
+ {TF,Name,{'DEFAULT',check_type(S,#typedef{typespec=Type},Type)}};
+ _ -> F
+ end;
+ _ -> F
+ end,
+ check_class_fields(S,Fields,[NewField|Acc]);
+check_class_fields(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+maybe_unchecked_OCFT(S,Type) ->
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=undefined} ->
+ check_type(S,#typedef{typespec=Type},Type);
+ _ ->
+ Type
+ end.
+
+if_current_checked_type(S,#type{def=Def}) ->
+ CurrentModule = S#state.mname,
+ CurrentCheckedName = S#state.tname,
+ MergedModules = S#state.inputmodules,
+ % CurrentCheckedModule = S#state.mname,
+ case Def of
+ #'Externaltypereference'{module=CurrentModule,
+ type=CurrentCheckedName} ->
+ true;
+ #'Externaltypereference'{module=ModuleName,
+ type=CurrentCheckedName} ->
+ case MergedModules of
+ undefined ->
+ false;
+ _ ->
+ lists:member(ModuleName,MergedModules)
+ end;
+ _ ->
+ false
+ end.
+
+
+
+check_pobject(_S,PObject) when is_record(PObject,pobjectdef) ->
+ Def = PObject#pobjectdef.def,
+ Def.
+
+
+check_pobjectset(S,PObjSet) ->
+ #pvaluesetdef{pos=Pos,name=Name,args=Args,type=Type,
+ valueset=ValueSet}=PObjSet,
+ {Mod,Def} = get_referenced_type(S,Type#type.def),
+ case Def of
+ #classdef{} ->
+ ClassName = #'Externaltypereference'{module=Mod,
+ type=get_datastr_name(Def)},
+ {valueset,Set} = ValueSet,
+% ObjectSet = #'ObjectSet'{class={objectclassname,ClassName},
+ ObjectSet = #'ObjectSet'{class=ClassName,
+ set=Set},
+ #pobjectsetdef{pos=Pos,name=Name,args=Args,class=Type#type.def,
+ def=ObjectSet};
+ _ ->
+ PObjSet
+ end.
+
+check_object(_S,ObjDef,ObjSpec) when (ObjDef#typedef.checked == true) ->
+ ObjSpec;
+check_object(S,_ObjDef,#'Object'{classname=ClassRef,def=ObjectDef}) ->
+ ?dbg("check_object ~p~n",[ObjectDef]),
+%% io:format("check_object,object: ~p~n",[ObjectDef]),
+% {MName,_ClassDef} = get_referenced_type(S,ClassRef),
+ NewClassRef = check_externaltypereference(S,ClassRef),
+ ClassDef =
+ case get_referenced_type(S,ClassRef) of
+ {MName,ClDef=#classdef{checked=false}} ->
+ NewState = update_state(S#state{type=ClDef,
+ tname=ClassRef#'Externaltypereference'.type},MName),
+ ObjClass=
+ check_class(NewState,ClDef),
+ #classdef{checked=true,
+ typespec=ObjClass};
+ {_,_ClDef} when is_record(_ClDef,classdef) ->
+ _ClDef;
+ {MName,_TDef=#typedef{checked=false,pos=Pos,
+ name=_TName,typespec=TS}} ->
+ ClDef = #classdef{pos=Pos,name=_TName,typespec=TS},
+ NewState = update_state(S#state{type=_TDef,
+ tname=ClassRef#'Externaltypereference'.type},MName),
+ ObjClass =
+ check_class(NewState,ClDef),
+ ClDef#classdef{checked=true,typespec=ObjClass};
+ {_,_ClDef} ->
+ _ClDef
+ end,
+ NewObj =
+ case ObjectDef of
+ Def when is_tuple(Def), (element(1,Def)==object) ->
+ NewSettingList = check_objectdefn(S,Def,ClassDef),
+ #'Object'{def=NewSettingList};
+ {po,{object,DefObj},ArgsList} ->
+ {_,Object} = get_referenced_type(S,DefObj),%DefObj is a
+ %%#'Externalvaluereference' or a #'Externaltypereference'
+ %% Maybe this call should be catched and in case of an exception
+ %% a not initialized parameterized object should be returned.
+ instantiate_po(S,ClassDef,Object,ArgsList);
+ {pv,{simpledefinedvalue,ObjRef},ArgList} ->
+ {_,Object} = get_referenced_type(S,ObjRef),
+ instantiate_po(S,ClassDef,Object,ArgList);
+ #'Externalvaluereference'{} ->
+ {_,Object} = get_referenced_type(S,ObjectDef),
+ check_object(S,Object,Object#typedef.typespec);
+ [] ->
+ %% An object with no fields. All class fields must be
+ %% optional or default. Check that all fields in
+ %% class are 'OPTIONAL' or 'DEFAULT'
+ class_fields_optional_check(S,ClassDef),
+ #'Object'{def={object,defaultsyntax,[]}};
+ _ ->
+ exit({error,{no_object,ObjectDef},S})
+ end,
+ Gen = gen_incl(S,NewObj#'Object'.def,
+ (ClassDef#classdef.typespec)#objectclass.fields),
+ NewObj#'Object'{classname=NewClassRef,gen=Gen};
+
+
+check_object(S,
+ _ObjSetDef,
+ ObjSet=#'ObjectSet'{class=ClassRef}) ->
+%% io:format("check_object,SET: ~p~n",[ObjSet#'ObjectSet'.set]),
+ ?dbg("check_object set: ~p~n",[ObjSet#'ObjectSet'.set]),
+ {_,ClassDef} = get_referenced_type(S,ClassRef),
+ NewClassRef = check_externaltypereference(S,ClassRef),
+ %% XXXXXXXXXX
+ case ObjSet of
+ #'ObjectSet'{set={'Externaltypereference',undefined,'MSAccessProtocol',
+ 'AllOperations'}} ->
+ ok;
+ _ ->
+ ok
+ end,
+ {UniqueFieldName,UniqueInfo} =
+ case (catch get_unique_fieldname(S,ClassDef)) of
+ {error,'__undefined_',_} ->
+ {{unique,undefined},{unique,undefined}};
+ {asn1,Msg,_} -> error({class,Msg,S});
+ {'EXIT',Msg} -> error({class,{internal_error,Msg},S});
+ Other -> {element(1,Other),Other}
+ end,
+ NewObjSet=
+ case prepare_objset(ObjSet#'ObjectSet'.set) of
+ {set,SET,EXT} ->
+ CheckedSet = check_object_list(S,NewClassRef,SET),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueInfo),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=extensionmark(NewSet,EXT)};
+
+ {'SingleValue',ERef = #'Externalvaluereference'{}} ->
+ {RefedMod,ObjDef} = get_referenced_type(S,ERef),
+ #'Object'{def=CheckedObj} =
+ check_object(S,ObjDef,ObjDef#typedef.typespec),
+
+ NewSet = get_unique_valuelist(S,[{{RefedMod,get_datastr_name(ObjDef)},
+ CheckedObj}],
+ UniqueInfo),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ ['EXTENSIONMARK'] ->
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=['EXTENSIONMARK']};
+
+ OSref when is_record(OSref,'Externaltypereference') ->
+ {_,OS=#typedef{typespec=OSdef}} = get_referenced_type(S,OSref),
+ check_object(S,OS,OSdef);
+
+ {Type,{'EXCEPT',Exclusion}} when is_record(Type,type) ->
+ {_,TDef} = get_referenced_type(S,Type#type.def),
+ OS = TDef#typedef.typespec,
+ NewSet = reduce_objectset(OS#'ObjectSet'.set,Exclusion),
+ NewOS = OS#'ObjectSet'{set=NewSet},
+ check_object(S,TDef#typedef{typespec=NewOS},
+ NewOS);
+ #type{def={pt,DefinedObjSet,ParamList}} ->
+ {_,PObjSetDef} = get_referenced_type(S,DefinedObjSet),
+ NewParamList =
+ [match_parameters(S,TmpParam,S#state.parameters)||
+ TmpParam <- ParamList],
+ instantiate_pos(S,ClassRef,PObjSetDef,NewParamList);
+
+ %% actually this is an ObjectSetFromObjects construct, it
+ %% is when the object set is retrieved from an object
+ %% field.
+ #type{def=#'ObjectClassFieldType'{classname=ObjName,
+ fieldname=FieldName}} ->
+ {RefedObjMod,TDef} = get_referenced_type(S,ObjName),
+ OS=TDef#typedef.typespec,
+ %% should get the right object set here. Get the field
+ %% FieldName out of the object set OS of class
+ %% OS#'ObjectSet'.class
+ OS2=check_object(S,TDef,OS),
+ NewSet=object_set_from_objects(S,RefedObjMod,FieldName,OS2),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ {'ObjectSetFromObjects',{_,_,ObjName},FieldName} ->
+ {RefedObjMod,TDef} = get_referenced_type(S,ObjName),
+ OS=TDef#typedef.typespec,
+ %% should get the right object set here. Get the field
+ %% FieldName out of the object set OS of class
+ %% OS#'ObjectSet'.class
+ OS2=check_object(S,TDef,OS),
+ NewSet=object_set_from_objects(S,RefedObjMod,FieldName,OS2),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ {'ObjectSetFromObjects',{_,ObjName},FieldName} ->
+ %% This is a ObjectSetFromObjects, i.e.
+ %% ObjectSetFromObjects ::= ReferencedObjects "." FieldName
+ %% with a defined object as ReferencedObjects. And
+ %% the FieldName of the Class (object) contains an object set.
+ {RefedObjMod,TDef} = get_referenced_type(S,ObjName),
+ O1 = TDef#typedef.typespec,
+ O2 = check_object(S,TDef,O1),
+ NewSet = object_set_from_objects(S,RefedObjMod,FieldName,O2),
+ OS2=ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet},
+ %%io:format("ObjectSet: ~p~n",[OS2]),
+ OS2;
+ {pos,{objectset,_,DefinedObjSet},Params} ->
+ {_,PObjSetDef} = get_referenced_type(S,DefinedObjSet),
+ NewParamList =
+ [match_parameters(S,TmpParam,S#state.parameters)||
+ TmpParam <- Params],
+ instantiate_pos(S,ClassRef,PObjSetDef,NewParamList);
+ Unknown ->
+ exit({error,{unknown_object_set,Unknown},S})
+ end,
+ NewSet2 = remove_duplicate_objects(NewObjSet#'ObjectSet'.set),
+ NewObjSet2 = NewObjSet#'ObjectSet'{set=NewSet2},
+ Gen = gen_incl_set(S,NewObjSet2#'ObjectSet'.set,
+ ClassDef),
+ ?dbg("check_object done~n",[]),
+ NewObjSet2#'ObjectSet'{class=NewClassRef,gen=Gen}.
+
+%% remove_duplicate_objects/1 remove duplicates of objects.
+%% For instance may Set contain objects of same class from
+%% different object sets that in fact might be duplicates.
+remove_duplicate_objects(Set) when is_list(Set) ->
+ Pred = fun({A,B,_},{A,C,_}) when B =< C -> true;
+ ({A,_,_},{B,_,_}) when A < B -> true;
+ ('EXTENSIONMARK','EXTENSIONMARK') -> true;
+ (T,A) when is_tuple(T),is_atom(A) -> true;% EXTENSIONMARK last in list
+ (_,_) -> false
+ end,
+ lists:usort(Pred,Set).
+
+%%
+extensionmark(L,true) ->
+ case lists:member('EXTENSIONMARK',L) of
+ true -> L;
+ _ -> L ++ ['EXTENSIONMARK']
+ end;
+extensionmark(L,_) ->
+ L.
+
+object_to_check(#typedef{typespec=ObjDef}) ->
+ ObjDef;
+object_to_check(#valuedef{type=ClassName,value=ObjectRef}) ->
+ %% If the object definition is parsed as an object the ClassName
+ %% is parsed as a type
+ #'Object'{classname=ClassName#type.def,def=ObjectRef}.
+
+prepare_objset({'SingleValue',Set}) when is_list(Set) ->
+ {set,Set,false};
+prepare_objset(L=['EXTENSIONMARK']) ->
+ L;
+prepare_objset(Set) when is_list(Set) ->
+ {set,Set,false};
+prepare_objset({{'SingleValue',Set},Ext}) ->
+ {set,merge_sets(Set,Ext),true};
+%%prepare_objset({Set,Ext}) when is_list(Set),is_list(Ext) ->
+%% {set,lists:append([Set,Ext]),true};
+prepare_objset({Set,Ext}) when is_list(Set) ->
+ {set,merge_sets(Set,Ext),true};
+prepare_objset({ObjDef={object,definedsyntax,_ObjFields},_Ext}) ->
+ {set,[ObjDef],true};
+prepare_objset(ObjDef={object,definedsyntax,_ObjFields}) ->
+ {set,[ObjDef],false};
+prepare_objset({ObjDef=#type{},Ext}) when is_list(Ext) ->
+ {set,[ObjDef|Ext],true};
+prepare_objset(Ret) ->
+ Ret.
+
+class_fields_optional_check(S,#classdef{typespec=ClassSpec}) ->
+ Fields = ClassSpec#objectclass.fields,
+ class_fields_optional_check1(S,Fields).
+
+class_fields_optional_check1(_S,[]) ->
+ ok;
+class_fields_optional_check1(S,[{typefield,_,'OPTIONAL'}|Rest]) ->
+ class_fields_optional_check1(S,Rest);
+class_fields_optional_check1(S,[{fixedtypevaluefield,_,_,_,'OPTIONAL'}|Rest]) ->
+ class_fields_optional_check1(S,Rest);
+class_fields_optional_check1(S,[{fixedtypevaluesetfield,_,_,'OPTIONAL'}|Rest]) ->
+ class_fields_optional_check1(S,Rest);
+class_fields_optional_check1(S,[{objectfield,_,_,_,'OPTIONAL'}|Rest]) ->
+ class_fields_optional_check1(S,Rest);
+class_fields_optional_check1(S,[{objectsetfield,_,_,'OPTIONAL'}|Rest]) ->
+ class_fields_optional_check1(S,Rest).
+
+%% ObjectSetFromObjects functionality
+
+%% The fieldname is a list of field names.They may be objects or
+%% object sets. If ObjectSet is an object set the resulting object set
+%% is the union of object sets if the last field name is an object
+%% set. If the last field is an object the resulting object set is
+%% the set of objects in ObjectSet.
+object_set_from_objects(S,RefedObjMod,FieldName,ObjectSet) ->
+ object_set_from_objects(S,RefedObjMod,FieldName,ObjectSet,[]).
+object_set_from_objects(S,RefedObjMod,FieldName,ObjectSet,InterSect)
+ when is_record(ObjectSet,'ObjectSet') ->
+ #'ObjectSet'{class=Cl,set=Set} = ObjectSet,
+ {_,ClassDef} = get_referenced_type(S,Cl),
+ object_set_from_objects(S,RefedObjMod,ClassDef,FieldName,Set,InterSect,[]);
+object_set_from_objects(S,RefedObjMod,FieldName,Object,InterSect)
+ when is_record(Object,'Object') ->
+ #'Object'{classname=Cl,def=Def}=Object,
+ object_set_from_objects(S,RefedObjMod,Cl,FieldName,[Def],InterSect,[]).
+object_set_from_objects(S,RefedObjMod,ClassDef,FieldName,['EXTENSIONMARK'|Os],
+ InterSect,Acc) ->
+ object_set_from_objects(S,RefedObjMod,ClassDef,FieldName,Os,InterSect,%%Acc);
+ ['EXTENSIONMARK'|Acc]);
+object_set_from_objects(S,RefedObjMod,ClassDef,FieldName,[O|Os],InterSect,Acc) ->
+ case object_set_from_objects2(S,mod_of_obj(RefedObjMod,element(1,O)),
+ ClassDef,FieldName,element(3,O),InterSect) of
+ ObjS when is_list(ObjS) ->
+ object_set_from_objects(S,RefedObjMod,ClassDef,FieldName,Os,InterSect,ObjS++Acc);
+ Obj ->
+ object_set_from_objects(S,RefedObjMod,ClassDef,FieldName,Os,InterSect,[Obj|Acc])
+ end;
+object_set_from_objects(_S,_RefedObjMod,_ClassDef,_FieldName,[],InterSect,Acc) ->
+ %% For instance may Acc contain objects of same class from
+ %% different object sets that in fact might be duplicates.
+ remove_duplicate_objects(osfo_intersection(InterSect,Acc)).
+%% Acc.
+object_set_from_objects2(S,RefedObjMod,ClassDef,[{valuefieldreference,OName}],
+ Fields,_InterSect) ->
+ %% this is an object
+ case lists:keysearch(OName,1,Fields) of
+ {value,{_,TDef}} ->
+ mk_object_set_from_object(S,RefedObjMod,TDef,ClassDef);
+ _ ->
+ [] % it may be an absent optional field
+ end;
+object_set_from_objects2(S,RefedObjMod,ClassDef,[{typefieldreference,OSName}],
+ Fields,_InterSect) ->
+ %% this is an object set
+ case lists:keysearch(OSName,1,Fields) of
+ {value,{_,TDef}} ->
+ case TDef#typedef.typespec of
+ #'ObjectSet'{class=_NextClName,set=NextSet} ->%% = TDef#typedef.typespec,
+ NextSet;
+ #'Object'{def=_ObjDef} ->
+ mk_object_set_from_object(S,RefedObjMod,TDef,ClassDef)
+%% ObjDef
+ %% error({error,{internal,unexpected_object,TDef}})
+ end;
+ _ ->
+ [] % it may be an absent optional field
+ end;
+object_set_from_objects2(S,RefedObjMod,_ClassDef,[{valuefieldreference,OName}|Rest],
+ Fields,InterSect) ->
+ %% this is an object
+ case lists:keysearch(OName,1,Fields) of
+ {value,{_,TDef}} ->
+ #'Object'{classname=NextClName,def=ODef}=TDef#typedef.typespec,
+ {_,_,NextFields}=ODef,
+ {_,NextClass} = get_referenced_type(S,NextClName),
+ object_set_from_objects2(S,RefedObjMod,NextClass,Rest,NextFields,InterSect);
+ _ ->
+ []
+ end;
+object_set_from_objects2(S,RefedObjMod,_ClassDef,[{typefieldreference,OSName}|Rest],
+ Fields,InterSect) ->
+ %% this is an object set
+ Next = {NextClName,NextSet} =
+ case lists:keysearch(OSName,1,Fields) of
+ {value,{_,TDef}} when is_record(TDef,'ObjectSet') ->
+ #'ObjectSet'{class=NextClN,set=NextS} = TDef,
+ {NextClN,NextS};
+ {value,{_,#typedef{typespec=OS}}} ->
+ %% objectsets in defined syntax will come here as typedef{}
+ %% #'ObjectSet'{class=NextClN,set=NextS} = OS,
+ case OS of
+ #'ObjectSet'{class=NextClN,set=NextS} ->
+ {NextClN,NextS};
+ #'Object'{classname=NextClN,def=NextDef} ->
+ {NextClN,[NextDef]}
+ end;
+ _ ->
+ {[],[]}
+ end,
+ case Next of
+ {[],[]} ->
+ [];
+ _ ->
+ {_,NextClass} = get_referenced_type(S,NextClName),
+ object_set_from_objects(S,RefedObjMod,NextClass,Rest,NextSet,InterSect,[])
+ end.
+
+mk_object_set_from_object(S,RefedObjMod,TDef,Class) ->
+ #'Object'{classname=_NextClName,def=ODef} = TDef#typedef.typespec,
+ {_,_,NextFields}=ODef,
+
+ UniqueFieldName =
+ case (catch get_unique_fieldname(S,Class)) of
+ {error,'__undefined_',_} -> {unique,undefined};
+ {asn1,Msg,_} -> error({class,Msg,S});
+ {'EXIT',Msg} -> error({class,{internal_error,Msg},S});
+ {Other,_} -> Other
+ end,
+ VDef = get_unique_value(S,NextFields,UniqueFieldName),
+ %% XXXXXXXXXXX
+ case VDef of
+ [] ->
+ ['EXTENSIONMARK'];
+ _ ->
+ {{RefedObjMod,get_datastr_name(TDef)},VDef,NextFields}
+ end.
+
+
+mod_of_obj(_RefedObjMod,{NewMod,ObjName})
+ when is_atom(NewMod),is_atom(ObjName) ->
+ NewMod;
+mod_of_obj(RefedObjMod,_) ->
+ RefedObjMod.
+
+
+merge_sets(Root,{'SingleValue',Ext}) ->
+ merge_sets(Root,Ext);
+merge_sets(Root,Ext) when is_list(Root),is_list(Ext) ->
+ Root ++ Ext;
+merge_sets(Root,Ext) when is_list(Ext) ->
+ [Root|Ext];
+merge_sets(Root,Ext) when is_list(Root) ->
+ Root++[Ext];
+merge_sets(Root,Ext) ->
+ [Root]++[Ext].
+
+reduce_objectset(ObjectSet,Exclusion) ->
+ case Exclusion of
+ {'SingleValue',#'Externalvaluereference'{value=Name}} ->
+ case lists:keysearch(Name,1,ObjectSet) of
+ {value,El} ->
+ lists:subtract(ObjectSet,[El]);
+ _ ->
+ ObjectSet
+ end
+ end.
+
+%% Checks a list of objects or object sets and returns a list of selected
+%% information for the code generation.
+check_object_list(S,ClassRef,ObjectList) ->
+ check_object_list(S,ClassRef,ObjectList,[]).
+
+check_object_list(S,ClassRef,[ObjOrSet|Objs],Acc) ->
+ ?dbg("check_object_list: ~p~n",[ObjOrSet]),
+ case ObjOrSet of
+ ObjDef when is_tuple(ObjDef),(element(1,ObjDef)==object) ->
+ Def =
+ check_object(S,#typedef{typespec=ObjDef},
+% #'Object'{classname={objectclassname,ClassRef},
+ #'Object'{classname=ClassRef,
+ def=ObjDef}),
+ check_object_list(S,ClassRef,Objs,[{{no_mod,no_name},Def#'Object'.def}|Acc]);
+ {'SingleValue',Ref = #'Externalvaluereference'{}} ->
+ ?dbg("{SingleValue,Externalvaluereference}~n",[]),
+ {RefedMod,ObjName,
+ #'Object'{def=Def}} = check_referenced_object(S,Ref),
+ check_object_list(S,ClassRef,Objs,[{{RefedMod,ObjName},Def}|Acc]);
+ ObjRef when is_record(ObjRef,'Externalvaluereference') ->
+ ?dbg("Externalvaluereference~n",[]),
+ {RefedMod,ObjName,
+ #'Object'{def=Def}} = check_referenced_object(S,ObjRef),
+ check_object_list(S,ClassRef,Objs,[{{RefedMod,ObjName},Def}|Acc]);
+ {'ValueFromObject',{_,Object},FieldName} ->
+ {_,Def} = get_referenced_type(S,Object),
+ TypeDef = get_fieldname_element(S,Def,FieldName),
+ (TypeDef#typedef.typespec)#'ObjectSet'.set;
+ ObjSet when is_record(ObjSet,type) ->
+ ObjSetDef =
+ case ObjSet#type.def of
+ Ref when is_record(Ref,typereference);
+ is_record(Ref,'Externaltypereference') ->
+ {_,D} = get_referenced_type(S,ObjSet#type.def),
+ D;
+ Other ->
+ throw({asn1_error,{'unknown objecset',Other,S}})
+ end,
+ #'ObjectSet'{set=ObjectsInSet} =
+ check_object(S,ObjSetDef,ObjSetDef#typedef.typespec),
+ AccList = transform_set_to_object_list(ObjectsInSet,[]),
+ check_object_list(S,ClassRef,Objs,AccList++Acc);
+ union ->
+ check_object_list(S,ClassRef,Objs,Acc);
+ {pos,{objectset,_,DefinedObjectSet},Params} ->
+ OSDef = #type{def={pt,DefinedObjectSet,Params}},
+ #'ObjectSet'{set=Set} =
+ check_object(S,ObjOrSet,#'ObjectSet'{class=ClassRef,
+ set=OSDef}),
+ check_object_list(S,ClassRef,Objs,Set ++ Acc);
+ {pv,{simpledefinedvalue,DefinedObject},Params} ->
+ Args = [match_parameters(S,Param,S#state.parameters)||
+ Param<-Params],
+ #'Object'{def=Def} =
+ check_object(S,ObjOrSet,
+ #'Object'{classname=ClassRef ,
+ def={po,{object,DefinedObject},
+ Args}}),
+ check_object_list(S,ClassRef,Objs,[{{no_mod,no_name},Def}|Acc]);
+ {'ObjectSetFromObjects',Os,FieldName} when is_tuple(Os) ->
+ NewSet =
+ check_ObjectSetFromObjects(S,element(size(Os),Os),
+ FieldName,[]),
+ check_object_list(S,ClassRef,Objs,NewSet++Acc);
+ {{'ObjectSetFromObjects',Os,FieldName},InterSection}
+ when is_tuple(Os) ->
+ NewSet =
+ check_ObjectSetFromObjects(S, element(size(Os),Os),
+ FieldName,InterSection),
+ check_object_list(S,ClassRef,Objs,NewSet++Acc);
+ Other ->
+ exit({error,{'unknown object',Other},S})
+ end;
+%% Finally reverse the accumulated list and if there are any extension
+%% marks in the object set put one indicator of that in the end of the
+%% list.
+check_object_list(_,_,[],Acc) ->
+ lists:reverse(Acc).
+
+check_referenced_object(S,ObjRef)
+ when is_record(ObjRef,'Externalvaluereference')->
+ case get_referenced_type(S,ObjRef) of
+ {RefedMod,ObjectDef} when is_record(ObjectDef,valuedef) ->
+ ?dbg("Externalvaluereference, ObjectDef: ~p~n",[ObjectDef]),
+ #type{def=ClassRef} = ObjectDef#valuedef.type,
+ Def = ObjectDef#valuedef.value,
+ {RefedMod,get_datastr_name(ObjectDef),
+ check_object(update_state(S,RefedMod),ObjectDef,#'Object'{classname=ClassRef,
+ def=Def})};
+ {RefedMod,ObjectDef} when is_record(ObjectDef,typedef) ->
+ {RefedMod,get_datastr_name(ObjectDef),
+ check_object(update_state(S,RefedMod),ObjectDef,ObjectDef#typedef.typespec)}
+ end.
+
+check_ObjectSetFromObjects(S,ObjName,FieldName,InterSection) ->
+ {RefedMod,TDef} = get_referenced_type(S,ObjName),
+ ObjOrSet = check_object(update_state(S,RefedMod),TDef,TDef#typedef.typespec),
+ InterSec = prepare_intersection(S,InterSection),
+ _NewSet = object_set_from_objects(S,RefedMod,FieldName,ObjOrSet,InterSec).
+
+prepare_intersection(_S,[]) ->
+ [];
+prepare_intersection(S,{'EXCEPT',ObjRef}) ->
+ except_names(S,ObjRef);
+prepare_intersection(_S,T) ->
+ exit({error,{internal_error,not_implemented,object_set_from_objects,T}}).
+except_names(_S,{'SingleValue',#'Externalvaluereference'{value=ObjName}}) ->
+ [{except,ObjName}];
+except_names(_,T) ->
+ exit({error,{internal_error,not_implemented,object_set_from_objects,T}}).
+
+osfo_intersection(InterSect,ObjList) ->
+ Res = [X|| X = {{_,N},_,_} <- ObjList,
+ lists:member({except,N},InterSect) == false],
+ case lists:member('EXTENSIONMARK',ObjList) of
+ true ->
+ Res ++ ['EXTENSIONMARK'];
+ _ ->
+ Res
+ end.
+
+%% get_fieldname_element/3
+%% gets the type/value/object/... of the referenced element in FieldName
+%% FieldName is a list and may have more than one element.
+%% Each element in FieldName can be either {typefieldreference,AnyFieldName}
+%% or {valuefieldreference,AnyFieldName}
+%% Def is the def of the first object referenced by FieldName
+get_fieldname_element(S,Def,[{_RefType,FieldName}]) when is_record(Def,typedef) ->
+ {_,_,ObjComps} = (Def#typedef.typespec)#'Object'.def,
+ check_fieldname_element(S,lists:keysearch(FieldName,1,ObjComps));
+get_fieldname_element(S,Def,[{_RefType,FieldName}|Rest])
+ when is_record(Def,typedef) ->
+ %% As FieldName is followd by other FieldNames it has to be an
+ %% object or objectset.
+ {_,_,ObjComps} = (Def#typedef.typespec)#'Object'.def,
+ NewDef = check_fieldname_element(S,lists:keysearch(FieldName,1,ObjComps)),
+ ObjDef = fun(#'Object'{def=D}) -> D;
+ (#'ObjectSet'{set=Set}) -> Set
+ end
+ (NewDef),
+ case ObjDef of
+ L when is_list(L) ->
+ [get_fieldname_element(S,X,Rest) || X <- L];
+ _ ->
+ get_fieldname_element(S,ObjDef,Rest)
+ end;
+get_fieldname_element(S,{object,_,Fields},[{_RefType,FieldName}|Rest]) ->
+ NewDef = check_fieldname_element(S,lists:keysearch(FieldName,1,Fields)),
+ get_fieldname_element(S,NewDef,Rest);
+get_fieldname_element(_S,Def,[]) ->
+ Def;
+get_fieldname_element(_S,Def,[{_RefType,_FieldName}|_RestFName])
+ when is_record(Def,typedef) ->
+ ok.
+
+check_fieldname_element(S,{value,{_,Def}}) ->
+ check_fieldname_element(S,Def);
+check_fieldname_element(S,TDef) when is_record(TDef,typedef) ->
+ check_type(S,TDef,TDef#typedef.typespec);
+check_fieldname_element(S,VDef) when is_record(VDef,valuedef) ->
+ check_value(S,VDef);
+check_fieldname_element(S,Eref)
+ when is_record(Eref,'Externaltypereference');
+ is_record(Eref,'Externalvaluereference') ->
+ {_,TDef}=get_referenced_type(S,Eref),
+ check_fieldname_element(S,TDef);
+check_fieldname_element(S,Other) ->
+ throw({error,{assigned_object_error,"not_assigned_object",Other,S}}).
+
+transform_set_to_object_list([{Name,_UVal,Fields}|Objs],Acc) ->
+ transform_set_to_object_list(Objs,[{Name,{object,generatesyntax,Fields}}|Acc]);
+transform_set_to_object_list(['EXTENSIONMARK'|Objs],Acc) ->
+%% transform_set_to_object_list(Objs,['EXTENSIONMARK'|Acc]);
+ transform_set_to_object_list(Objs,Acc);
+transform_set_to_object_list([],Acc) ->
+ Acc.
+
+get_unique_valuelist(_S,ObjSet,{unique,undefined}) -> % no unique field in object
+ lists:map(fun({N,{_,_,F}})->{N,no_unique_value,F};
+ (V={_,_,_}) ->V;
+ ({A,B}) -> {A,no_unique_value,B}
+ end, ObjSet);
+get_unique_valuelist(S,ObjSet,{UFN,Opt}) ->
+ get_unique_vlist(S,ObjSet,UFN,Opt,[]).
+
+
+get_unique_vlist(_S,[],_,_,[]) ->
+ ['EXTENSIONMARK'];
+get_unique_vlist(S,[],_,Opt,Acc) ->
+ case catch check_uniqueness(remove_duplicate_objects(Acc)) of
+ {asn1_error,_} when Opt =/= 'OPTIONAL' ->
+ error({'ObjectSet',"not unique objects in object set",S});
+ {asn1_error,_} ->
+ lists:reverse(Acc);
+ _ ->
+ lists:reverse(Acc)
+ end;
+get_unique_vlist(S,['EXTENSIONMARK'|Rest],UniqueFieldName,Opt,Acc) ->
+ get_unique_vlist(S,Rest,UniqueFieldName,Opt,Acc);
+get_unique_vlist(S,[{ObjName,Obj}|Rest],UniqueFieldName,Opt,Acc) ->
+ {_,_,Fields} = Obj,
+ NewObjInf =
+ case get_unique_value(S,Fields,UniqueFieldName) of
+ #valuedef{value=V} -> [{ObjName,V,Fields}];
+ [] -> []; % maybe the object only was a reference to an
+ % empty object set.
+ no_unique_value -> [{ObjName,no_unique_value,Fields}]
+ end,
+ get_unique_vlist(S,Rest,UniqueFieldName,Opt,NewObjInf++Acc);
+
+get_unique_vlist(S,[V={_,_,_}|Rest],UniqueFieldName,Opt,Acc) ->
+ get_unique_vlist(S,Rest,UniqueFieldName,Opt,[V|Acc]).
+
+get_unique_value(S,Fields,UniqueFieldName) ->
+ Module = S#state.mname,
+ case lists:keysearch(UniqueFieldName,1,Fields) of
+ {value,Field} ->
+ case element(2,Field) of
+ VDef when is_record(VDef,valuedef) ->
+ VDef;
+ {'ValueFromObject',Object,Name} ->
+ case Object of
+ {object,Ext} when is_record(Ext,'Externaltypereference') ->
+ OtherModule = Ext#'Externaltypereference'.module,
+ ExtObjName = Ext#'Externaltypereference'.type,
+ ObjDef = asn1_db:dbget(OtherModule,ExtObjName),
+ ObjSpec = ObjDef#typedef.typespec,
+ get_unique_value(OtherModule,element(3,ObjSpec),Name);
+ {object,{_,_,ObjName}} ->
+ ObjDef = asn1_db:dbget(Module,ObjName),
+ ObjSpec = ObjDef#typedef.typespec,
+ get_unique_value(Module,element(3,ObjSpec),Name);
+ {po,Object,_Params} ->
+ exit({error,{'parameterized object not implemented yet',
+ Object},S})
+ end;
+ Value when is_atom(Value);is_number(Value) ->
+ #valuedef{value=Value,module=Module};
+ {'CHOICE',{C,Value}} when is_atom(C) ->
+ %% #valuedef{value=normalize_value(S,element(3,Field),VDef,[])}
+ case Value of
+ Scalar when is_atom(Scalar);is_number(Scalar) ->
+ #valuedef{value=Value,module=Module};
+ Eref = #'Externalvaluereference'{} ->
+ element(2,get_referenced_type(S,Eref))
+ end
+ end;
+ false ->
+ case Fields of
+ [{_,#typedef{typespec=#'ObjectSet'{set=['EXTENSIONMARK']}}}] ->
+ [];
+ _ ->
+ no_unique_value
+ end
+ end.
+
+check_uniqueness(NameValueList) ->
+ check_uniqueness1(lists:keysort(2,NameValueList)).
+
+check_uniqueness1([]) ->
+ true;
+check_uniqueness1([_]) ->
+ true;
+check_uniqueness1([{_,N,_},{_,N,_}|_Rest]) ->
+ throw({asn1_error,{'objects in set must have unique values in UNIQUE fields',N}});
+check_uniqueness1([_|Rest]) ->
+ check_uniqueness1(Rest).
+
+%% instantiate_po/4
+%% ClassDef is the class of Object,
+%% Object is the Parameterized object, which is referenced,
+%% ArgsList is the list of actual parameters
+%% returns an #'Object' record.
+instantiate_po(S=#state{parameters=_OldArgs},_ClassDef,Object,ArgsList) when is_record(Object,pobjectdef) ->
+ FormalParams = get_pt_args(Object),
+ MatchedArgs = match_args(S,FormalParams,ArgsList,[]),
+% NewS = S#state{type=Object,parameters=MatchedArgs++OldArgs},
+ NewS = S#state{type=Object,parameters=MatchedArgs},
+ check_object(NewS,Object,#'Object'{classname=Object#pobjectdef.class,
+ def=Object#pobjectdef.def}).
+
+%% instantiate_pos/4
+%% ClassDef is the class of ObjectSetDef,
+%% ObjectSetDef is the Parameterized object set, which is referenced
+%% on the right side of the assignment,
+%% ArgsList is the list of actual parameters, i.e. real objects
+instantiate_pos(S=#state{parameters=_OldArgs},ClassRef,ObjectSetDef,ArgsList) ->
+% ClassName = ClassDef#classdef.name,
+ FormalParams = get_pt_args(ObjectSetDef),
+ OSet = case get_pt_spec(ObjectSetDef) of
+ {valueset,Set} ->
+% #'ObjectSet'{class=name2Extref(S#state.mname,
+% ClassName),set=Set};
+ #'ObjectSet'{class=ClassRef,set=Set};
+ Set when is_record(Set,'ObjectSet') -> Set;
+ _ ->
+ error({type,"parameterized object set failure",S})
+ end,
+ MatchedArgs = match_args(S,FormalParams,ArgsList,[]),
+% NewS = S#state{type=ObjectSetDef,parameters=MatchedArgs++OldArgs},
+ NewS = S#state{type=ObjectSetDef,parameters=MatchedArgs},
+ check_object(NewS,ObjectSetDef,OSet).
+
+
+%% gen_incl -> boolean()
+%% If object with Fields has any of the corresponding class' typefields
+%% then return value is true otherwise it is false.
+%% If an object lacks a typefield but the class has a type field that
+%% is OPTIONAL then we want gen to be true
+gen_incl(S,{_,_,Fields},CFields)->
+ gen_incl1(S,Fields,CFields).
+
+gen_incl1(_,_,[]) ->
+ false;
+gen_incl1(S,Fields,[C|CFields]) ->
+ case element(1,C) of
+ typefield ->
+ true; %% should check that field is OPTIONAL or DEFUALT if
+ %% the object lacks this field
+ objectfield ->
+ case lists:keysearch(element(2,C),1,Fields) of
+ {value,Field} ->
+ ClassRef = case element(3,C) of
+ #type{def=Ref} -> Ref;
+ Eref when is_record(Eref,'Externaltypereference') ->
+ Eref
+ end,
+ ClassFields = get_objclass_fields(S,ClassRef),
+ ObjDef =
+ case element(2,Field) of
+ TDef when is_record(TDef,typedef) ->
+ check_object(S,TDef,TDef#typedef.typespec);
+ ERef ->
+ {_,T} = get_referenced_type(S,ERef),
+ check_object(S,T,object_to_check(T))
+ end,
+ case gen_incl(S,ObjDef#'Object'.def,
+ ClassFields) of
+ true ->
+ true;
+ _ ->
+ gen_incl1(S,Fields,CFields)
+ end;
+ _ ->
+ gen_incl1(S,Fields,CFields)
+ end;
+ _ ->
+ gen_incl1(S,Fields,CFields)
+ end.
+
+get_objclass_fields(S,Eref=#'Externaltypereference'{}) ->
+ {_,ClassDef} = get_referenced_type(S,Eref),
+ get_objclass_fields(S,ClassDef);
+get_objclass_fields(S,CD=#classdef{typespec=#'Externaltypereference'{}}) ->
+ get_objclass_fields(S,CD#classdef.typespec);
+get_objclass_fields(_,#classdef{typespec=CDef})
+ when is_record(CDef,objectclass) ->
+ CDef#objectclass.fields.
+
+
+%% first if no unique field in the class return false.(don't generate code)
+gen_incl_set(S,Fields,#typedef{typespec=#type{def=Eref}})
+ when is_record(Eref,'Externaltypereference') ->
+ %% When a Defined class is a reference toanother class definition
+ {_,CDef} = get_referenced_type(S,Eref),
+ gen_incl_set(S,Fields,CDef);
+gen_incl_set(S,Fields,ClassDef) ->
+ case catch get_unique_fieldname(S,ClassDef) of
+ Tuple when is_tuple(Tuple), size(Tuple) =:= 3 ->
+ false;
+ _ ->
+ gen_incl_set1(S,Fields,
+ (ClassDef#classdef.typespec)#objectclass.fields)
+ end.
+
+
+%% if any of the existing or potentially existing objects has a typefield
+%% then return true.
+gen_incl_set1(_,[],_CFields)->
+ false;
+gen_incl_set1(_,['EXTENSIONMARK'],_) ->
+ true;
+%% Fields are the fields of an object in the object set.
+%% CFields are the fields of the class of the object set.
+gen_incl_set1(_,['EXTENSIONMARK'|_],_) ->
+ true;
+gen_incl_set1(S,[Object|Rest],CFields)->
+ Fields = element(size(Object),Object),
+ case gen_incl1(S,Fields,CFields) of
+ true ->
+ true;
+ false ->
+ gen_incl_set1(S,Rest,CFields)
+ end.
+
+check_objectdefn(S,Def,CDef) when is_record(CDef,classdef) ->
+ WithSyntax = (CDef#classdef.typespec)#objectclass.syntax,
+ ClassFields = (CDef#classdef.typespec)#objectclass.fields,
+ case Def of
+ {object,defaultsyntax,Fields} ->
+ check_defaultfields(S,Fields,ClassFields);
+ {object,definedsyntax,Fields} ->
+ {_,WSSpec} = WithSyntax,
+ NewFields =
+ case catch( convert_definedsyntax(S,Fields,WSSpec,
+ ClassFields,[])) of
+ {asn1,{_ErrorType,ObjToken,ClassToken}} ->
+ throw({asn1,{'match error in object',ObjToken,
+ 'found in object',ClassToken,'found in class'}});
+ Err={asn1,_} -> throw(Err);
+ Err={'EXIT',_} -> throw(Err);
+ DefaultFields when is_list(DefaultFields) ->
+ DefaultFields
+ end,
+ {object,defaultsyntax,NewFields};
+ {object,_ObjectId} -> % This is a DefinedObject
+ fixa;
+ Other ->
+ exit({error,{objectdefn,Other}})
+ end.
+
+check_defaultfields(S,Fields,ClassFields) ->
+ check_defaultfields(S,Fields,ClassFields,[]).
+
+check_defaultfields(_S,[],_ClassFields,Acc) ->
+ {object,defaultsyntax,lists:reverse(Acc)};
+check_defaultfields(S,[{FName,Spec}|Fields],ClassFields,Acc) ->
+ case lists:keysearch(FName,2,ClassFields) of
+ {value,CField} ->
+ {NewField,RestFields} =
+ convert_to_defaultfield(S,FName,[Spec|Fields],CField),
+ check_defaultfields(S,RestFields,ClassFields,[NewField|Acc]);
+ _ ->
+ throw({error,{asn1,{'unvalid field in object',FName}}})
+ end.
+%% {object,defaultsyntax,Fields}.
+
+convert_definedsyntax(_S,[],[],_ClassFields,Acc) ->
+ lists:reverse(Acc);
+convert_definedsyntax(S,Fields,WithSyntax,ClassFields,Acc) ->
+ {MatchedField,RestFields,RestWS} =
+ match_field(S,Fields,WithSyntax,ClassFields),
+ if
+ is_list(MatchedField) ->
+ convert_definedsyntax(S,RestFields,RestWS,ClassFields,
+ lists:append(MatchedField,Acc));
+ true ->
+ convert_definedsyntax(S,RestFields,RestWS,ClassFields,
+ [MatchedField|Acc])
+ end.
+
+match_field(S,Fields,WithSyntax,ClassFields) ->
+ match_field(S,Fields,WithSyntax,ClassFields,[]).
+
+match_field(S,Fields,[W|Ws],ClassFields,Acc) when is_list(W) ->
+ case catch(match_optional_field(S,Fields,W,ClassFields,[])) of
+ {'EXIT',_} ->
+ match_field(Fields,Ws,ClassFields,Acc); %% add S
+%% {[Result],RestFields} ->
+%% {Result,RestFields,Ws};
+ {Result,RestFields} when is_list(Result) ->
+ {Result,RestFields,Ws};
+ _ ->
+ match_field(S,Fields,Ws,ClassFields,Acc)
+ end;
+match_field(S,Fields,WithSyntax,ClassFields,_Acc) ->
+ match_mandatory_field(S,Fields,WithSyntax,ClassFields,[]).
+
+match_optional_field(_S,RestFields,[],_,Ret) ->
+ {Ret,RestFields};
+%% An additional optional field within an optional field
+match_optional_field(S,Fields,[W|Ws],ClassFields,Ret) when is_list(W) ->
+ case catch match_optional_field(S,Fields,W,ClassFields,[]) of
+ {'EXIT',_} when length(Ws) > 0 ->
+ match_optional_field(S,Fields,Ws,ClassFields,Ret);
+ {'EXIT',_} ->
+ {Ret,Fields};
+ {asn1,{optional_matcherror,_,_}} when length(Ws) > 0 ->
+ match_optional_field(S,Fields,Ws,ClassFields,Ret);
+ {asn1,{optional_matcherror,_,_}} ->
+ {Ret,Fields};
+ {OptionalField,RestFields} ->
+ match_optional_field(S,RestFields,Ws,ClassFields,
+ lists:append(OptionalField,Ret))
+ end;
+%% identify and skip word
+match_optional_field(S,[{_,_,#'Externaltypereference'{type=WorS}}|Rest],
+ [WorS|Ws],ClassFields,Ret) ->
+ match_optional_field(S,Rest,Ws,ClassFields,Ret);
+match_optional_field(S,[],_,ClassFields,Ret) ->
+ match_optional_field(S,[],[],ClassFields,Ret);
+%% identify and skip comma
+match_optional_field(S,[{WorS,_}|Rest],[{WorS,_}|Ws],ClassFields,Ret) ->
+ match_optional_field(S,Rest,Ws,ClassFields,Ret);
+%% am optional setting inside another optional setting may be "double-listed"
+match_optional_field(S,[Setting],DefinedSyntax,ClassFields,Ret)
+ when is_list(Setting) ->
+ match_optional_field(S,Setting,DefinedSyntax,ClassFields,Ret);
+%% identify and save field data
+match_optional_field(S,[Setting|Rest],[{_,W}|Ws],ClassFields,Ret) ->
+ ?dbg("matching optional field setting: ~p with user friendly syntax: ~p~n",[Setting,W]),
+ WorS =
+ case Setting of
+ Type when is_record(Type,type) -> Type;
+ {'ValueFromObject',_,_} -> Setting;
+ {object,_,_} -> Setting;
+ {_,_,WordOrSetting} -> WordOrSetting;
+ Other -> Other
+ end,
+ case lists:keysearch(W,2,ClassFields) of
+ false ->
+ throw({asn1,{optional_matcherror,WorS,W}});
+ {value,CField} ->
+ {NewField,RestFields} =
+ convert_to_defaultfield(S,W,[WorS|Rest],CField),
+ match_optional_field(S,RestFields,Ws,ClassFields,[NewField|Ret])
+ end;
+match_optional_field(_S,[WorS|_Rest],[W|_Ws],_ClassFields,_Ret) ->
+ throw({asn1,{optional_matcherror,WorS,W}}).
+
+match_mandatory_field(_S,[],[],_,[Acc]) ->
+ {Acc,[],[]};
+match_mandatory_field(_S,[],[],_,Acc) ->
+ {Acc,[],[]};
+match_mandatory_field(S,[],[H|T],CF,Acc) when is_list(H) ->
+ match_mandatory_field(S,[],T,CF,Acc);
+match_mandatory_field(_S,[],WithSyntax,_,_Acc) ->
+ throw({asn1,{mandatory_matcherror,[],WithSyntax}});
+%match_mandatory_field(_S,Fields,WithSyntax=[W|_Ws],_ClassFields,[Acc]) when is_list(W) ->
+match_mandatory_field(_S,Fields,WithSyntax=[W|_Ws],_ClassFields,Acc) when is_list(W), length(Acc) >= 1 ->
+ {Acc,Fields,WithSyntax};
+%% identify and skip word
+%%match_mandatory_field(S,[{_,_,WorS}|Rest],
+match_mandatory_field(S,[{_,_,#'Externaltypereference'{type=WorS}}|Rest],
+ [WorS|Ws],ClassFields,Acc) ->
+ match_mandatory_field(S,Rest,Ws,ClassFields,Acc);
+%% identify and skip comma
+match_mandatory_field(S,[{WorS,_}|Rest],[{WorS,_}|Ws],ClassFields,Ret) ->
+ match_mandatory_field(S,Rest,Ws,ClassFields,Ret);
+%% identify and save field data
+match_mandatory_field(S,[Setting|Rest],[{_,W}|Ws],ClassFields,Acc) ->
+ ?dbg("matching field setting: ~p with user friendly syntax: ~p~n",[Setting,W]),
+ WorS =
+ case Setting of
+ {object,_,_} -> Setting;
+ {_,_,WordOrSetting} -> WordOrSetting;
+ Type when is_record(Type,type) -> Type;
+ Other -> Other
+ end,
+ case lists:keysearch(W,2,ClassFields) of
+ false ->
+ throw({asn1,{mandatory_matcherror,WorS,W}});
+ {value,CField} ->
+ {NewField,RestFields} =
+ convert_to_defaultfield(S,W,[WorS|Rest],CField),
+ match_mandatory_field(S,RestFields,Ws,ClassFields,[NewField|Acc])
+ end;
+
+match_mandatory_field(_S,[WorS|_Rest],[W|_Ws],_ClassFields,_Acc) ->
+ throw({asn1,{mandatory_matcherror,WorS,W}}).
+
+%% Converts a field of an object from defined syntax to default syntax
+%% A field may be a type, a fixed type value, an object, an objectset,
+%%
+convert_to_defaultfield(S,ObjFieldName,[OFS|RestOFS],CField)->
+ ?dbg("convert field: ~p of type: ~p~n",[ObjFieldName,element(1,CField)]),
+ CurrMod = S#state.mname,
+ Strip_value_tag =
+ fun({value_tag,ValueSetting}) -> ValueSetting;
+ (VS) -> VS
+ end,
+ ObjFieldSetting = Strip_value_tag(OFS),
+ RestSettings = [Strip_value_tag(X)||X <- RestOFS],
+ case element(1,CField) of
+ typefield ->
+ TypeDef=
+ case ObjFieldSetting of
+ TypeRec when is_record(TypeRec,type) -> TypeRec#type.def;
+ TDef when is_record(TDef,typedef) ->
+ TDef#typedef{checked=true,
+ typespec=check_type(S,TDef,
+ TDef#typedef.typespec)};
+ _ -> ObjFieldSetting
+ end,
+ {Type,SettingsLeft} =
+ if
+ is_record(TypeDef,typedef) -> {TypeDef,RestSettings};
+ is_record(TypeDef,'ObjectClassFieldType') ->
+ T=check_type(S,#typedef{typespec=ObjFieldSetting},ObjFieldSetting),
+ {oCFT_def(S,T),RestSettings};
+% #typedef{checked=true,name=Name,typespec=IT};
+ is_tuple(TypeDef), element(1,TypeDef) == pt ->
+ %% this is an inlined type. If constructed
+ %% type save in data base
+ T=check_type(S,#typedef{typespec=ObjFieldSetting},ObjFieldSetting),
+ #'Externaltypereference'{type=PtName} =
+ element(2,TypeDef),
+ NameList = [PtName,S#state.tname],
+ NewName = list_to_atom(asn1ct_gen:list2name(NameList)),
+ NewTDef=#typedef{checked=true,name=NewName,
+ typespec=T},
+ asn1_db:dbput(S#state.mname,NewName,NewTDef),
+ %%asn1ct_gen:insert_once(parameterized_objects,{NewName,type,NewTDef}),
+ insert_once(S,parameterized_objects,
+ {NewName,type,NewTDef}),
+ {NewTDef,RestSettings};
+ is_tuple(TypeDef), element(1,TypeDef)=='SelectionType' ->
+ T=check_type(S,#typedef{typespec=ObjFieldSetting},
+ ObjFieldSetting),
+ Name = type_name(S,T),
+ {#typedef{checked=true,name=Name,typespec=T},RestSettings};
+ true ->
+ case asn1ct_gen:type(asn1ct_gen:get_inner(TypeDef)) of
+ ERef = #'Externaltypereference'{module=CurrMod} ->
+ {RefMod,T} = get_referenced_type(S,ERef),
+ check_and_save(S,ERef#'Externaltypereference'{module=RefMod},T,RestSettings);
+
+ ERef = #'Externaltypereference'{} ->
+ {RefMod,T} = get_referenced_type(S,ERef),
+ check_and_save(S,ERef#'Externaltypereference'{module=RefMod},T,RestSettings);
+ Bif when Bif=={primitive,bif};Bif=={constructed,bif} ->
+ T = check_type(S,#typedef{typespec=ObjFieldSetting},
+ ObjFieldSetting),
+ {#typedef{checked=true,name=Bif,typespec=T},RestSettings};
+ _OCFT = #'ObjectClassFieldType'{} ->
+ T=check_type(S,#typedef{typespec=ObjFieldSetting},ObjFieldSetting),
+ %%io:format("OCFT=~p~n,T=~p~n",[OCFT,T]),
+ {#typedef{checked=true,typespec=T},RestSettings};
+ _ ->
+ %this case should not happen any more
+ {Mod,T} =
+ get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=ObjFieldSetting}),
+ case Mod of
+ CurrMod ->
+ {T,RestSettings};
+ ExtMod ->
+ #typedef{name=Name} = T,
+ {T#typedef{name={ExtMod,Name}},RestSettings}
+ end
+ end
+ end,
+ {{ObjFieldName,Type},SettingsLeft};
+ fixedtypevaluefield ->
+ case ObjFieldName of
+ Val when is_atom(Val) ->
+ %% ObjFieldSetting can be a value,an objectidentifiervalue,
+ %% an element in an enumeration or namednumberlist etc.
+ ValRef =
+ case ObjFieldSetting of
+ ValSetting=#'Externalvaluereference'{} ->
+ ValSetting;
+ {'ValueFromObject',{_,ObjRef},FieldName} ->
+ {_,Object} = get_referenced_type(S,ObjRef),
+ ChObject = check_object(S,Object,
+ Object#typedef.typespec),
+ get_fieldname_element(S,Object#typedef{typespec=ChObject},
+ FieldName);
+ ValSetting = #valuedef{} ->
+ ValSetting;
+ ValSetting = {'CHOICE',{Alt,_ChVal}} when is_atom(Alt) ->
+ #valuedef{type=element(3,CField),
+ value=ValSetting,
+ module=S#state.mname};
+ ValSetting ->
+ #identifier{val=ValSetting}
+ end,
+ ?dbg("fixedtypevaluefield ValRef: ~p~n",[ValRef]),
+ case ValRef of
+ #valuedef{} ->
+ {{ObjFieldName,check_value(S,ValRef)},RestSettings};
+ _ ->
+ ValDef =
+ case catch get_referenced_type(S,ValRef) of
+ {error,_} ->
+ NewValDef =
+ #valuedef{name=Val,
+ type=element(3,CField),
+ value=ObjFieldSetting,
+ module=S#state.mname},
+ check_value(S,NewValDef);
+ {M,VDef} when is_record(VDef,valuedef) ->
+ check_value(update_state(S,M),
+ %%S#state{mname=M},
+ VDef);%% XXX
+ {M,VDef} ->
+ check_value(update_state(S,M),
+ %%S#state{mname=M},
+ #valuedef{name=Val,
+ type=element(3,CField),
+ value=VDef,
+ module=M})
+ end,
+ {{ObjFieldName,ValDef},RestSettings}
+ end;
+ Val ->
+ {{ObjFieldName,Val},RestSettings}
+ end;
+ fixedtypevaluesetfield ->
+ {{ObjFieldName,ObjFieldSetting},RestSettings};
+ objectfield ->
+ CheckObject =
+ fun(O) ->
+ O#typedef{checked=true,typespec=
+ check_object(S,O,O#typedef.typespec)}
+ end,
+ ObjectSpec =
+ case ObjFieldSetting of
+ Ref when is_record(Ref,'Externalvaluereference') ->
+ %% The object O might be a #valuedef{} if
+ %% e.g. the definition looks like
+ %% myobj SOMECLASS ::= referencedObject
+ {M,O} = get_referenced_type(S,Ref),
+ check_object(S,O,object_to_check(O)),
+ Ref#'Externalvaluereference'{module=M};
+
+ {'ValueFromObject',{_,ObjRef},FieldName} ->
+ %% This is an ObjectFromObject
+ {_,Object} = get_referenced_type(S,ObjRef),
+ ChObject = check_object(S,Object,
+ Object#typedef.typespec),
+ ObjFromObj=
+ get_fieldname_element(S,Object#typedef{
+ typespec=ChObject},
+ FieldName),
+ CheckObject(ObjFromObj);
+ ObjDef={object,_,_} ->
+ %% An object defined inlined in another object
+ %% class is an objectfield, that implies that
+ %% {objectsetfield,TypeFieldName,DefinedObjecClass,
+ %% OptionalitySpec}
+ %% DefinedObjecClass = #'Externaltypereference'{}|
+ %% 'TYPE-IDENTIFIER' | 'ABSTRACT-SYNTAX'
+ ClassName = element(3,CField),
+ InlinedObjName=
+ list_to_atom(lists:concat([S#state.tname]++
+ ['_',ObjFieldName])),
+
+ ObjSpec = #'Object'{classname=ClassName,
+ def=ObjDef},
+ CheckedObj=
+ check_object(S,#typedef{typespec=ObjSpec},ObjSpec),
+ InlObj = #typedef{checked=true,name=InlinedObjName,
+ typespec=CheckedObj},
+ ObjKey = {InlinedObjName,InlinedObjName},
+ %% asn1ct_gen:insert_once(inlined_objects,ObjKey),
+ insert_once(S,inlined_objects,ObjKey),
+ %% Which module to use here? Could it be other than top_module ?
+ %% asn1_db:dbput(S#state.mname,InlinedObjName,InlObj),
+ asn1_db:dbput(get(top_module),InlinedObjName,InlObj),
+ InlObj;
+ #type{def=Eref} when is_record(Eref,'Externaltypereference') ->
+ {_,O} = get_referenced_type(S,Eref),
+ CheckObject(O);
+ Other ->
+ {_,O} = get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=Other}),
+ CheckObject(O)
+ end,
+ {{ObjFieldName,ObjectSpec},RestSettings};
+ variabletypevaluefield ->
+ {{ObjFieldName,ObjFieldSetting},RestSettings};
+ variabletypevaluesetfield ->
+ {{ObjFieldName,ObjFieldSetting},RestSettings};
+%% objectset_or_fixedtypevalueset_field ->
+%% ok;
+ objectsetfield ->
+ ObjSetSpec = get_objectset_def(S,ObjFieldSetting,CField),
+ ?dbg("objectsetfield, ObjSetSpec:~p~n",[ObjSetSpec]),
+ {{ObjFieldName,
+ ObjSetSpec#typedef{checked=true,
+ typespec=check_object(S,ObjSetSpec,
+ ObjSetSpec#typedef.typespec)}},RestSettings}
+ end.
+
+get_objectset_def(S,Ref,CField)
+ when is_record(Ref,'Externaltypereference');
+ is_record(Ref,'Externalvaluereference') ->
+ {_M,T}=get_referenced_type(S,Ref),
+ get_objectset_def2(S,T,CField);
+get_objectset_def(S,ObjectList,CField) when is_list(ObjectList) ->
+ %% an objctset defined in the object,though maybe
+ %% parsed as a SequenceOfValue
+ %% The ObjectList may be a list of references to
+ %% objects, a ValueFromObject
+ ?dbg("objectsetfield: ~p~n",[CField]),
+ get_objectset_def2(S,ObjectList,CField);
+get_objectset_def(S,'EXTENSIONMARK',CField) ->
+ ?dbg("objectsetfield: ~p~n",[CField]),
+ get_objectset_def2(S,['EXTENSIONMARK'],CField);
+get_objectset_def(_S,ObjFieldSetting={'SingleValue',_},CField) ->
+ %% a Union of defined objects
+ ?dbg("objectsetfield, SingleValue~n",[]),
+ union_of_defed_objs(CField,ObjFieldSetting);
+get_objectset_def(_S,ObjFieldSetting={{'SingleValue',_},_},CField) ->
+ %% a Union of defined objects
+ ?dbg("objectsetfield, SingleValue~n",[]),
+ union_of_defed_objs(CField,ObjFieldSetting);
+get_objectset_def(S,{object,_,[#type{def={'TypeFromObject',
+ {object,RefedObj},
+ FieldName}}]},_CField) ->
+ %% This case occurs when an ObjectSetFromObjects
+ %% production is used
+ {_M,Def} = get_referenced_type(S,RefedObj),
+ get_fieldname_element(S,Def,FieldName);
+get_objectset_def(S,{object,_,[{setting,_,ERef}]},CField)
+ when is_record(ERef,'Externaltypereference') ->
+ {_,T} = get_referenced_type(S,ERef),
+ get_objectset_def2(S,T,CField);
+get_objectset_def(S,#type{def=ERef},_CField)
+ when is_record(ERef,'Externaltypereference') ->
+ {_,T} = get_referenced_type(S,ERef),
+ T;
+get_objectset_def(S,ObjFieldSetting,CField)
+ when is_atom(ObjFieldSetting) ->
+ ERef = #'Externaltypereference'{module=S#state.mname,
+ type=ObjFieldSetting},
+ {_,T} = get_referenced_type(S,ERef),
+ get_objectset_def2(S,T,CField).
+
+get_objectset_def2(_S,T = #typedef{typespec=#'Object'{}},_CField) ->
+ #typedef{typespec=#'Object'{classname=Class,def=Def}} = T,
+ T#typedef{typespec=#'ObjectSet'{class=Class,set=[Def]}};
+get_objectset_def2(_S,Set,CField) when is_list(Set) ->
+ {_,_,Type,_} = CField,
+ ClassDef = Type#type.def,
+ #typedef{typespec=#'ObjectSet'{class=ClassDef,
+ set=Set}};
+get_objectset_def2(_S,T = #typedef{typespec=#'ObjectSet'{}},_CField) ->
+ T;
+get_objectset_def2(_S,T,_CField) ->
+ io:format("Warning get_objectset_def2: uncontrolled object set structure:~n~p~n",[T]).
+
+type_name(S,#type{def=Def}) ->
+ CurrMod = S#state.mname,
+ case asn1ct_gen:type(asn1ct_gen:get_inner(Def)) of
+ #'Externaltypereference'{module=CurrMod,type=Name} ->
+ Name;
+ #'Externaltypereference'{module=Mod,type=Name} ->
+ {Mod,Name};
+ Bif when Bif=={primitive,bif};Bif=={constructed,bif} ->
+ Bif
+ end.
+
+merged_name(#state{inputmodules=[]},ERef) ->
+ ERef;
+merged_name(S,ERef=#'Externaltypereference'{module=M}) ->
+ case {S#state.mname,lists:member(M,S#state.inputmodules)} of
+ {M,_} ->
+ ERef;
+ {MergeM,true} ->
+ %% maybe the reference is renamed
+ NewName = renamed_reference(S,ERef),
+ ERef#'Externaltypereference'{module=MergeM,type=NewName};
+ {_,_} -> % i.e. M /= MergeM, not an inputmodule
+ ERef
+ end.
+
+oCFT_def(S,T) ->
+ case get_OCFT_inner(S,T) of
+ ERef=#'Externaltypereference'{} -> ERef;
+ {Name,Type} -> #typedef{checked=true,name=Name,typespec=Type};
+ 'ASN1_OPEN_TYPE' ->
+ #typedef{checked=true,typespec=T#type{def='ASN1_OPEN_TYPE'}}
+ end.
+
+get_OCFT_inner(_S,T) ->
+% Module=S#state.mname,
+ Def = T#type.def,
+ case Def#'ObjectClassFieldType'.type of
+ {fixedtypevaluefield,_,InnerType} ->
+ case asn1ct_gen:type(asn1ct_gen:get_inner(InnerType#type.def)) of
+ Bif when Bif=={primitive,bif};Bif=={constructed,bif} ->
+ {Bif,InnerType};
+ ERef = #'Externaltypereference'{} ->
+ ERef
+ end;
+ 'ASN1_OPEN_TYPE' -> 'ASN1_OPEN_TYPE'
+ end.
+
+
+
+union_of_defed_objs({_,_,_ObjClass=#type{def=ClassDef},_},ObjFieldSetting) ->
+ #typedef{typespec=#'ObjectSet'{class = ClassDef,
+ set = ObjFieldSetting}};
+union_of_defed_objs({_,_,DefObjClassRef,_},ObjFieldSetting)
+ when is_record(DefObjClassRef,'Externaltypereference') ->
+ #typedef{typespec=#'ObjectSet'{class = DefObjClassRef,
+ set = ObjFieldSetting}}.
+
+
+check_value(OldS,V) when is_record(V,pvaluesetdef) ->
+ #pvaluesetdef{checked=Checked,type=Type} = V,
+ case Checked of
+ true -> V;
+ {error,_} -> V;
+ false ->
+ case get_referenced_type(OldS,Type#type.def) of
+ {_,Class} when is_record(Class,classdef) ->
+ throw({pobjectsetdef});
+ _ -> continue
+ end
+ end;
+check_value(_OldS,V) when is_record(V,pvaluedef) ->
+ %% Fix this case later
+ V;
+check_value(OldS,V) when is_record(V,typedef) ->
+ %% This case when a value set has been parsed as an object set.
+ %% It may be a value set
+ ?dbg("check_value, V: ~p~n",[V]),
+ #typedef{typespec=TS} = V,
+ case TS of
+ #'ObjectSet'{class=ClassRef} ->
+ {RefM,TSDef} = get_referenced_type(OldS,ClassRef),
+ %%IsObjectSet(TSDef);
+ case TSDef of
+ #classdef{} -> throw({objectsetdef});
+ #typedef{typespec=#type{def=Eref}} when
+ is_record(Eref,'Externaltypereference') ->
+ %% This case if the class reference is a defined
+ %% reference to class
+ check_value(OldS,V#typedef{typespec=TS#'ObjectSet'{class=Eref}});
+ #typedef{} ->
+ % an ordinary value set with a type in #typedef.typespec
+ ValueSet = TS#'ObjectSet'.set,
+ Type=check_type(OldS,TSDef,TSDef#typedef.typespec),
+ Value = check_value(OldS,#valuedef{type=Type,
+ value=ValueSet,
+ module=RefM}),
+ {valueset,Type#type{constraint=Value#valuedef.value}}
+ end;
+ _ ->
+ throw({objectsetdef})
+ end;
+check_value(S,#valuedef{pos=Pos,name=Name,type=Type,
+ value={valueset,Constr}}) ->
+ NewType = Type#type{constraint=[Constr]},
+ {valueset,
+ check_type(S,#typedef{pos=Pos,name=Name,typespec=NewType},NewType)};
+check_value(OldS=#state{recordtopname=TopName},V) when is_record(V,valuedef) ->
+ #valuedef{name=Name,checked=Checked,type=Vtype,
+ value=Value,module=ModName} = V,
+ ?dbg("check_value, V: ~p~n",[V]),
+ case Checked of
+ true ->
+ V;
+ {error,_} ->
+ V;
+ false ->
+ Def = Vtype#type.def,
+ Constr = Vtype#type.constraint,
+ S = OldS#state{type=Vtype,tname=Def,value=V,vname=Name},
+ SVal = update_state(S,ModName),
+ NewDef =
+ case Def of
+ Ext when is_record(Ext,'Externaltypereference') ->
+ RecName = Ext#'Externaltypereference'.type,
+ {RefM,Type} = get_referenced_type(S,Ext),
+ %% If V isn't a value but an object Type is a #classdef{}
+ %%NewS = S#state{mname=RefM},
+ NewS = update_state(S,RefM),
+ case Type of
+ #classdef{} ->
+ throw({objectdef});
+ #typedef{} ->
+ case is_contextswitchtype(Type) of
+ true ->
+ #valuedef{value=CheckedVal}=
+ check_value(NewS,V#valuedef{type=Type#typedef.typespec}),
+ #newv{value=CheckedVal};
+ _ ->
+ #valuedef{value=CheckedVal}=
+ check_value(NewS#state{recordtopname=[RecName|TopName]},
+ V#valuedef{type=Type#typedef.typespec}),
+ #newv{value=CheckedVal}
+ end;
+ #type{} ->
+ %% A parameter that couldn't be categorized.
+ #valuedef{value=CheckedVal}=
+ check_value(NewS#state{recordtopname=[RecName|TopName]},
+ V#valuedef{type=Type}),
+ #newv{value=CheckedVal}
+ end;
+ 'ANY' ->
+ case Value of
+ {opentypefieldvalue,ANYType,ANYValue} ->
+ CheckedV=
+ check_value(SVal,#valuedef{name=Name,
+ type=ANYType,
+ value=ANYValue,
+ module=ModName}),
+ #newv{value=CheckedV#valuedef.value};
+ _ ->
+ throw({error,{asn1,{'cant check value of type',Def}}})
+ end;
+ 'INTEGER' ->
+ ok=validate_integer(SVal,Value,[],Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ {'INTEGER',NamedNumberList} ->
+ ok=validate_integer(SVal,Value,NamedNumberList,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ {'BIT STRING',NamedNumberList} ->
+ ok=validate_bitstring(SVal,Value,NamedNumberList,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'NULL' ->
+ ok=validate_null(SVal,Value,Constr),
+ #newv{};
+ 'OBJECT IDENTIFIER' ->
+ {ok,_}=validate_objectidentifier(SVal,Value,Constr),
+ #newv{value = normalize_value(SVal,Vtype,Value,[])};
+ 'RELATIVE-OID' ->
+ {ok,_}=validate_relative_oid(SVal,Value,Constr),
+ #newv{value = Value};
+ 'ObjectDescriptor' ->
+ ok=validate_objectdescriptor(SVal,Value,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'REAL' ->
+ ok = validate_real(SVal,Value,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ {'ENUMERATED',NamedNumberList} ->
+ ok=validate_enumerated(SVal,Value,NamedNumberList,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'BOOLEAN'->
+ ok=validate_boolean(SVal,Value,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'OCTET STRING' ->
+ ok=validate_octetstring(SVal,Value,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'NumericString' ->
+ ok=validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ TString when TString =:= 'TeletexString';
+ TString =:= 'T61String' ->
+ ok=validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'VideotexString' ->
+ ok=validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'UTCTime' ->
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+% exit({'cant check value of type' ,Def});
+ 'GeneralizedTime' ->
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+% exit({'cant check value of type' ,Def});
+ 'GraphicString' ->
+ ok=validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'VisibleString' ->
+ ok=validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'GeneralString' ->
+ ok=validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'PrintableString' ->
+ ok=validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'IA5String' ->
+ ok=validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'BMPString' ->
+ ok=validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'UTF8String' ->
+ ok = validate_restrictedstring(SVal,Vtype,Value,Constr),
+ %%io:format("Vtype: ~p~nValue: ~p~n",[Vtype,Value]);
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ 'UniversalString' -> %added 6/12 -00
+ ok = validate_restrictedstring(SVal,Value,Def,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,[])};
+ Seq when is_record(Seq,'SEQUENCE') ->
+ {ok,SeqVal} = validate_sequence(SVal,Value,
+ Seq#'SEQUENCE'.components,
+ Constr),
+ #newv{value=normalize_value(SVal,Vtype,SeqVal,TopName)};
+ {'SEQUENCE OF',Components} ->
+ ok=validate_sequenceof(SVal,Value,Components,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,TopName)};
+ {'CHOICE',Components} ->
+ ok=validate_choice(SVal,Value,Components,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,TopName)};
+ Set when is_record(Set,'SET') ->
+ ok=validate_set(SVal,Value,Set#'SET'.components,
+ Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,TopName)};
+ {'SET OF',Components} ->
+ ok=validate_setof(SVal,Value,Components,Constr),
+ #newv{value=normalize_value(SVal,Vtype,Value,TopName)};
+ {'SelectionType',SelName,SelT} ->
+ CheckedT = check_selectiontype(SVal,SelName,SelT),
+ NewV = V#valuedef{type=CheckedT},
+ SelVDef=check_value(S#state{value=NewV},NewV),
+ #newv{value=SelVDef#valuedef.value};
+ Other ->
+ exit({'cannot check value of type' ,Other})
+ end,
+ case NewDef#newv.value of
+ unchanged ->
+ V#valuedef{checked=true,value=Value};
+ ok ->
+ V#valuedef{checked=true,value=Value};
+ {error,Reason} ->
+ V#valuedef{checked={error,Reason},value=Value};
+ _V ->
+ V#valuedef{checked=true,value=_V}
+ end
+ end.
+
+is_contextswitchtype(#typedef{name='EXTERNAL'})->
+ true;
+is_contextswitchtype(#typedef{name='EMBEDDED PDV'}) ->
+ true;
+is_contextswitchtype(#typedef{name='CHARACTER STRING'}) ->
+ true;
+is_contextswitchtype(_) ->
+ false.
+
+% validate_integer(S,{identifier,Pos,Id},NamedNumberList,Constr) ->
+% case lists:keysearch(Id,1,NamedNumberList) of
+% {value,_} -> ok;
+% false -> error({value,"unknown NamedNumber",S})
+% end;
+%% This case occurs when there is a valuereference
+%% validate_integer(S=#state{mname=M},
+%% #'Externalvaluereference'{module=M,value=Id}=Ref,
+validate_integer(S,#'Externalvaluereference'{value=Id}=Ref,
+ NamedNumberList,Constr) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> validate_integer_ref(S,Ref,NamedNumberList,Constr)
+ %%error({value,"unknown NamedNumber",S})
+ end;
+validate_integer(S,Id,NamedNumberList,Constr) when is_atom(Id) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> validate_integer_ref(S,Id,NamedNumberList,Constr)
+ %error({value,"unknown NamedNumber",S})
+ end;
+validate_integer(_S,Value,_NamedNumberList,Constr) when is_integer(Value) ->
+ check_integer_range(Value,Constr).
+
+validate_integer_ref(S,Id,_,_) when is_atom(Id) ->
+ error({value,"unknown integer referens",S});
+validate_integer_ref(S,Ref,NamedNumberList,Constr) ->
+ case get_referenced_type(S,Ref) of
+ {M,V} when is_record(V,valuedef) ->
+ NewS = update_state(S,M),
+ case check_value(NewS,V) of
+ #valuedef{type=#type{def='INTEGER'},value=Value} ->
+ validate_integer(NewS,Value,NamedNumberList,Constr);
+ _Err -> error({value,"unknown integer referens",S})
+ end;
+ _ ->
+ error({value,"unknown integer referens",S})
+ end.
+
+
+
+check_integer_range(Int,Constr) when is_list(Constr) ->
+ NewConstr = [X || #constraint{c=X} <- Constr],
+ check_constr(Int,NewConstr);
+
+check_integer_range(_Int,_Constr) ->
+ %%io:format("~p~n",[Constr]),
+ ok.
+
+check_constr(Int,[{'ValueRange',Lb,Ub}|T]) when Int >= Lb, Int =< Ub ->
+ check_constr(Int,T);
+check_constr(_Int,[]) ->
+ ok.
+
+validate_bitstring(_S,_Value,_NamedNumberList,_Constr) ->
+ ok.
+
+validate_null(_S,'NULL',_Constr) ->
+ ok.
+
+%%------------
+%% This can be removed when the old parser is removed
+%% The function removes 'space' atoms from the list
+
+is_space_list([H],Acc) ->
+ lists:reverse([H|Acc]);
+is_space_list([H,space|T],Acc) ->
+ is_space_list(T,[H|Acc]);
+is_space_list([],Acc) ->
+ lists:reverse(Acc);
+is_space_list([H|T],Acc) ->
+ is_space_list(T,[H|Acc]).
+
+validate_objectidentifier(S,ERef,C) ->
+ validate_objectidentifier(S,o_id,ERef,C).
+
+validate_objectidentifier(S,OID,ERef,C)
+ when is_record(ERef,'Externalvaluereference') ->
+ validate_objectidentifier(S,OID,[ERef],C);
+validate_objectidentifier(S,OID,Tup,C) when is_tuple(Tup) ->
+ validate_objectidentifier(S,OID,tuple_to_list(Tup),C);
+validate_objectidentifier(S,OID,L,_) ->
+ NewL = is_space_list(L,[]),
+ case validate_objectidentifier1(S,OID,NewL) of
+ NewL2 when is_list(NewL2) ->{ok,list_to_tuple(NewL2)};
+ Other -> {ok,Other}
+ end.
+
+validate_objectidentifier1(S, OID, [Id|T])
+ when is_record(Id,'Externalvaluereference') ->
+ case catch get_referenced_type(S,Id) of
+ {M,V} when is_record(V,valuedef) ->
+ NewS = update_state(S,M),
+ case check_value(NewS,V) of
+ #valuedef{type=#type{def=ERef},checked=true,
+ value=Value} when is_tuple(Value) ->
+ case is_object_id(OID,NewS,ERef) of
+ true ->
+ %% T must be a RELATIVE-OID
+ validate_oid(true,NewS, rel_oid, T, lists:reverse(tuple_to_list(Value)));
+ _ ->
+ error({value, {"illegal "++to_string(OID),[Id|T]}, S})
+ end;
+ _ ->
+ error({value, {"illegal "++to_string(OID),[Id|T]}, S})
+ end;
+ _ ->
+ validate_oid(true,S, OID, [Id|T], [])
+ end;
+validate_objectidentifier1(S,OID,V) ->
+ validate_oid(true,S,OID,V,[]).
+
+validate_oid(false, S, OID, V, Acc) ->
+ error({value, {"illegal "++to_string(OID), V,Acc}, S});
+validate_oid(_,_, _, [], Acc) ->
+ lists:reverse(Acc);
+validate_oid(_, S, OID, [Value|Vrest], Acc) when is_integer(Value) ->
+ validate_oid(valid_objectid(OID,Value,Acc),S, OID, Vrest, [Value|Acc]);
+validate_oid(_, S, OID, [{'NamedNumber',_Name,Value}|Vrest], Acc)
+ when is_integer(Value) ->
+ validate_oid(valid_objectid(OID,Value,Acc), S, OID, Vrest, [Value|Acc]);
+validate_oid(_, S, OID, [Id|Vrest], Acc)
+ when is_record(Id,'Externalvaluereference') ->
+ case catch get_referenced_type(S, Id) of
+ {M,V} when is_record(V,valuedef) ->
+ NewS = update_state(S,M),
+ NewVal = case check_value(NewS, V) of
+ #valuedef{checked=true,value=Value} ->
+ fun(Int) when is_integer(Int) -> [Int];
+ (L) when is_list(L) -> L;
+ (T) when is_tuple(T) -> tuple_to_list(T)
+ end (Value);
+ _ ->
+ error({value, {"illegal "++to_string(OID),
+ [Id|Vrest],Acc}, S})
+ end,
+ case NewVal of
+ List when is_list(List) ->
+ validate_oid(valid_objectid(OID,NewVal,Acc), NewS,
+ OID, Vrest,lists:reverse(NewVal)++Acc);
+ _ ->
+ NewVal
+ end;
+ _ ->
+ case reserved_objectid(Id#'Externalvaluereference'.value, Acc) of
+ Value when is_integer(Value) ->
+ validate_oid(valid_objectid(OID,Value,Acc),
+ S, OID,Vrest, [Value|Acc]);
+ false ->
+ error({value, {"illegal "++to_string(OID),[Id,Vrest],Acc}, S})
+ end
+ end;
+validate_oid(_, S, OID, [{Atom,Value}],[])
+ when is_atom(Atom),is_integer(Value) ->
+ %% this case when an OBJECT IDENTIFIER value has been parsed as a
+ %% SEQUENCE value
+ Rec = #'Externalvaluereference'{module=S#state.mname,
+ value=Atom},
+ validate_objectidentifier1(S, OID, [Rec,Value]);
+validate_oid(_, S, OID, [{Atom,EVRef}],[])
+ when is_atom(Atom),is_record(EVRef,'Externalvaluereference') ->
+ %% this case when an OBJECT IDENTIFIER value has been parsed as a
+ %% SEQUENCE value OTP-4354
+ Rec = #'Externalvaluereference'{module=EVRef#'Externalvaluereference'.module,
+ value=Atom},
+ validate_objectidentifier1(S, OID, [Rec,EVRef]);
+validate_oid(_, S, OID, [Atom|Rest],Acc) when is_atom(Atom) ->
+ Rec = #'Externalvaluereference'{module=S#state.mname,
+ value=Atom},
+ validate_oid(true,S, OID, [Rec|Rest],Acc);
+validate_oid(_, S, OID, V, Acc) ->
+ error({value, {"illegal "++to_string(OID),V,Acc},S}).
+
+validate_relative_oid(S,Value,Constr) ->
+ validate_objectidentifier(S,rel_oid,Value,Constr).
+
+is_object_id(OID,S,ERef=#'Externaltypereference'{}) ->
+ {_,OI} = get_referenced_type(S,ERef),
+ is_object_id(OID,S,OI#typedef.typespec);
+is_object_id(o_id,_S,'OBJECT IDENTIFIER') ->
+ true;
+is_object_id(rel_oid,_S,'RELATIVE-OID') ->
+ true;
+is_object_id(_,_S,'INTEGER') ->
+ true;
+is_object_id(OID,S,#type{def=Def}) ->
+ is_object_id(OID,S,Def);
+is_object_id(_,_S,_) ->
+ false.
+
+to_string(o_id) ->
+ "OBJECT IDENTIFIER";
+to_string(rel_oid) ->
+ "RELATIVE-OID".
+
+%% ITU-T Rec. X.680 Annex B - D
+reserved_objectid('itu-t',[]) -> 0;
+reserved_objectid('ccitt',[]) -> 0;
+%% arcs below "itu-t"
+reserved_objectid('recommendation',[0]) -> 0;
+reserved_objectid('question',[0]) -> 1;
+reserved_objectid('administration',[0]) -> 2;
+reserved_objectid('network-operator',[0]) -> 3;
+reserved_objectid('identified-organization',[0]) -> 4;
+%% arcs below "recommendation"
+reserved_objectid('a',[0,0]) -> 1;
+reserved_objectid('b',[0,0]) -> 2;
+reserved_objectid('c',[0,0]) -> 3;
+reserved_objectid('d',[0,0]) -> 4;
+reserved_objectid('e',[0,0]) -> 5;
+reserved_objectid('f',[0,0]) -> 6;
+reserved_objectid('g',[0,0]) -> 7;
+reserved_objectid('h',[0,0]) -> 8;
+reserved_objectid('i',[0,0]) -> 9;
+reserved_objectid('j',[0,0]) -> 10;
+reserved_objectid('k',[0,0]) -> 11;
+reserved_objectid('l',[0,0]) -> 12;
+reserved_objectid('m',[0,0]) -> 13;
+reserved_objectid('n',[0,0]) -> 14;
+reserved_objectid('o',[0,0]) -> 15;
+reserved_objectid('p',[0,0]) -> 16;
+reserved_objectid('q',[0,0]) -> 17;
+reserved_objectid('r',[0,0]) -> 18;
+reserved_objectid('s',[0,0]) -> 19;
+reserved_objectid('t',[0,0]) -> 20;
+reserved_objectid('u',[0,0]) -> 21;
+reserved_objectid('v',[0,0]) -> 22;
+reserved_objectid('w',[0,0]) -> 23;
+reserved_objectid('x',[0,0]) -> 24;
+reserved_objectid('y',[0,0]) -> 25;
+reserved_objectid('z',[0,0]) -> 26;
+
+
+reserved_objectid(iso,[]) -> 1;
+%% arcs below "iso", note that number 1 is not used
+reserved_objectid('standard',[1]) -> 0;
+reserved_objectid('member-body',[1]) -> 2;
+reserved_objectid('identified-organization',[1]) -> 3;
+
+reserved_objectid('joint-iso-itu-t',[]) -> 2;
+reserved_objectid('joint-iso-ccitt',[]) -> 2;
+
+reserved_objectid(_,_) -> false.
+
+valid_objectid(_OID,[],_Acc) ->
+ true;
+valid_objectid(OID,[H|T],Acc) ->
+ case valid_objectid(OID, H, Acc) of
+ true ->
+ valid_objectid(OID,T,[H|Acc]);
+ _ ->
+ false
+ end;
+valid_objectid(o_id,I,[]) when I =:= 0; I =:= 1; I =:= 2 -> true;
+valid_objectid(o_id,_I,[]) -> false;
+valid_objectid(o_id,I,[0]) when I >= 0; I =< 4 -> true;
+valid_objectid(o_id,_I,[0]) -> false;
+valid_objectid(o_id,I,[1]) when I =:= 0; I =:= 2; I =:= 3 -> true;
+valid_objectid(o_id,_I,[1]) -> false;
+valid_objectid(o_id,_I,[2]) -> true;
+valid_objectid(_,_,_) -> true.
+
+
+
+
+
+
+validate_objectdescriptor(_S,_Value,_Constr) ->
+ ok.
+
+validate_real(_S,_Value,_Constr) ->
+ ok.
+
+validate_enumerated(S,Id,NamedNumberList,_Constr) when is_atom(Id) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown ENUMERATED",S})
+ end;
+validate_enumerated(S,{identifier,_Pos,Id},NamedNumberList,_Constr) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown ENUMERATED",S})
+ end;
+validate_enumerated(S,#'Externalvaluereference'{value=Id},
+ NamedNumberList,_Constr) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown ENUMERATED",S})
+ end.
+
+validate_boolean(_S,_Value,_Constr) ->
+ ok.
+
+validate_octetstring(_S,_Value,_Constr) ->
+ ok.
+
+validate_restrictedstring(_S,_Value,_Def,_Constr) ->
+ ok.
+
+validate_sequence(S=#state{type=Vtype},Value,_Components,_Constr) ->
+ case Vtype of
+ #type{tag=[{tag,'UNIVERSAL',8,'IMPLICIT',32}]} ->
+ %% this is an 'EXTERNAL' (or INSTANCE OF)
+ case Value of
+ [{identification,_}|_RestVal] ->
+ {ok,to_EXTERNAL1990(S,Value)};
+ _ ->
+ {ok,Value}
+ end;
+ _ ->
+ {ok,Value}
+ end.
+
+validate_sequenceof(_S,_Value,_Components,_Constr) ->
+ ok.
+
+validate_choice(_S,_Value,_Components,_Constr) ->
+ ok.
+
+validate_set(_S,_Value,_Components,_Constr) ->
+ ok.
+
+validate_setof(_S,_Value,_Components,_Constr) ->
+ ok.
+
+to_EXTERNAL1990(S,[{identification,{'CHOICE',{syntax,Stx}}}|Rest]) ->
+ to_EXTERNAL1990(S,Rest,[{'direct-reference',Stx}]);
+to_EXTERNAL1990(S,[{identification,{'CHOICE',{'presentation-context-id',I}}}|Rest]) ->
+ to_EXTERNAL1990(S,Rest,[{'indirect-reference',I}]);
+to_EXTERNAL1990(S,[{identification,{'CHOICE',{'context-negotiation',[{_,PCid},{_,TrStx}]}}}|Rest]) ->
+ to_EXTERNAL1990(S,Rest,[{'indirect-reference',PCid},{'direct-reference',TrStx}]);
+to_EXTERNAL1990(S,_) ->
+ error({value,"illegal value in EXTERNAL type",S}).
+
+to_EXTERNAL1990(S,[V={'data-value-descriptor',_}|Rest],Acc) ->
+ to_EXTERNAL1990(S,Rest,[V|Acc]);
+to_EXTERNAL1990(_S,[{'data-value',Val}],Acc) ->
+ Encoding = {encoding,{'CHOICE',{'octet-aligned',Val}}},
+ lists:reverse([Encoding|Acc]);
+to_EXTERNAL1990(S,_,_) ->
+ error({value,"illegal value in EXTERNAL type",S}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Functions to normalize the default values of SEQUENCE
+%% and SET components into Erlang valid format
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+normalize_value(_,_,mandatory,_) ->
+ mandatory;
+normalize_value(_,_,'OPTIONAL',_) ->
+ 'OPTIONAL';
+normalize_value(S,Type,{'DEFAULT',Value},NameList) ->
+ case catch get_canonic_type(S,Type,NameList) of
+ {'BOOLEAN',CType,_} ->
+ normalize_boolean(S,Value,CType);
+ {'INTEGER',CType,_} ->
+ normalize_integer(S,Value,CType);
+ {'BIT STRING',CType,_} ->
+ normalize_bitstring(S,Value,CType);
+ {'OCTET STRING',CType,_} ->
+ normalize_octetstring(S,Value,CType);
+ {'NULL',_CType,_} ->
+ %%normalize_null(Value);
+ 'NULL';
+ {'RELATIVE-OID',_,_} ->
+ normalize_relative_oid(S,Value);
+ {'OBJECT IDENTIFIER',_,_} ->
+ normalize_objectidentifier(S,Value);
+ {'ObjectDescriptor',_,_} ->
+ normalize_objectdescriptor(Value);
+ {'REAL',_,_} ->
+ normalize_real(Value);
+ {'ENUMERATED',CType,_} ->
+ normalize_enumerated(Value,CType);
+ {'CHOICE',CType,NewNameList} ->
+ normalize_choice(S,Value,CType,NewNameList);
+ {'SEQUENCE',CType,NewNameList} ->
+ normalize_sequence(S,Value,CType,NewNameList);
+ {'SEQUENCE OF',CType,NewNameList} ->
+ normalize_seqof(S,Value,CType,NewNameList);
+ {'SET',CType,NewNameList} ->
+ normalize_set(S,Value,CType,NewNameList);
+ {'SET OF',CType,NewNameList} ->
+ normalize_setof(S,Value,CType,NewNameList);
+ {restrictedstring,CType,_} ->
+ normalize_restrictedstring(S,Value,CType);
+ {'ASN1_OPEN_TYPE',{typefield,_TF},NL} -> %an open type
+ normalize_objectclassfieldvalue(S,Value,NL);
+ Err ->
+ io:format("WARNING: could not check default value ~p~nType:~n~p~nNameList:~n~p~n",[Value,Type,Err]),
+ Value
+ end;
+normalize_value(S,Type,Val,NameList) ->
+ normalize_value(S,Type,{'DEFAULT',Val},NameList).
+
+normalize_boolean(S,{Name,Bool},CType) when is_atom(Name) ->
+ normalize_boolean(S,Bool,CType);
+normalize_boolean(_,true,_) ->
+ true;
+normalize_boolean(_,false,_) ->
+ false;
+normalize_boolean(S,Bool=#'Externalvaluereference'{},CType) ->
+ get_normalized_value(S,Bool,CType,fun normalize_boolean/3,[]);
+normalize_boolean(_,Other,_) ->
+ throw({error,{asn1,{'invalid default value',Other}}}).
+
+normalize_integer(_S,Int,_) when is_integer(Int) ->
+ Int;
+normalize_integer(_S,{Name,Int},_) when is_atom(Name),is_integer(Int) ->
+ Int;
+normalize_integer(S,{Name,Int=#'Externalvaluereference'{}},
+ Type) when is_atom(Name) ->
+ normalize_integer(S,Int,Type);
+normalize_integer(S,Int=#'Externalvaluereference'{value=Name},Type) ->
+ case Type of
+ NNL when is_list(NNL) ->
+ case lists:keysearch(Name,1,NNL) of
+ {value,{Name,Val}} ->
+ Val;
+ false ->
+ get_normalized_value(S,Int,Type,
+ fun normalize_integer/3,[])
+ end;
+ _ ->
+ get_normalized_value(S,Int,Type,fun normalize_integer/3,[])
+ end;
+normalize_integer(_,Int,_) ->
+ exit({'Unknown INTEGER value',Int}).
+
+normalize_bitstring(S,Value,Type)->
+ %% There are four different Erlang formats of BIT STRING:
+ %% 1 - a list of ones and zeros.
+ %% 2 - a list of atoms.
+ %% 3 - as an integer, for instance in hexadecimal form.
+ %% 4 - as a tuple {Unused, Binary} where Unused is an integer
+ %% and tells how many bits of Binary are unused.
+ %%
+ %% normalize_bitstring/3 transforms Value according to:
+ %% A to 3,
+ %% B to 1,
+ %% C to 1 or 3
+ %% D to 2,
+ %% Value can be on format:
+ %% A - {hstring, String}, where String is a hexadecimal string.
+ %% B - {bstring, String}, where String is a string on bit format
+ %% C - #'Externalvaluereference'{value=V}, where V is a defined value
+ %% D - list of #'Externalvaluereference', where each value component
+ %% is an identifier corresponing to NamedBits in Type.
+ %% E - list of ones and zeros, if Value already is normalized.
+ case Value of
+ {hstring,String} when is_list(String) ->
+ hstring_to_int(String);
+ {bstring,String} when is_list(String) ->
+ bstring_to_bitlist(String);
+ Rec when is_record(Rec,'Externalvaluereference') ->
+ get_normalized_value(S,Value,Type,
+ fun normalize_bitstring/3,[]);
+ RecList when is_list(RecList) ->
+ case Type of
+ NBL when is_list(NBL) ->
+ F = fun(#'Externalvaluereference'{value=Name}) ->
+ case lists:keysearch(Name,1,NBL) of
+ {value,{Name,_}} ->
+ Name;
+ Other ->
+ throw({error,Other})
+ end;
+ (I) when I =:= 1; I =:= 0 ->
+ I;
+ (Other) ->
+ throw({error,Other})
+ end,
+ case catch lists:map(F,RecList) of
+ {error,Reason} ->
+ io:format("WARNING: default value not "
+ "compatible with type definition ~p~n",
+ [Reason]),
+ Value;
+ NewList ->
+ NewList
+ end;
+ _ ->
+ io:format("WARNING: default value not "
+ "compatible with type definition ~p~n",
+ [RecList]),
+ Value
+ end;
+ {Name,String} when is_atom(Name) ->
+ normalize_bitstring(S,String,Type);
+ Other ->
+ io:format("WARNING: illegal default value ~p~n",[Other]),
+ Value
+ end.
+
+hstring_to_int(L) when is_list(L) ->
+ hstring_to_int(L,0).
+hstring_to_int([H|T],Acc) when H >= $A, H =< $F ->
+ hstring_to_int(T,(Acc bsl 4) + (H - $A + 10) ) ;
+hstring_to_int([H|T],Acc) when H >= $0, H =< $9 ->
+ hstring_to_int(T,(Acc bsl 4) + (H - $0));
+hstring_to_int([],Acc) ->
+ Acc.
+
+bstring_to_bitlist([H|T]) when H == $0; H == $1 ->
+ [H - $0 | bstring_to_bitlist(T)];
+bstring_to_bitlist([]) ->
+ [].
+
+%% normalize_octetstring/1 changes representation of input Value to a
+%% list of octets.
+%% Format of Value is one of:
+%% {bstring,String} each element in String corresponds to one bit in an octet
+%% {hstring,String} each element in String corresponds to one byte in an octet
+%% #'Externalvaluereference'
+normalize_octetstring(S,Value,CType) ->
+ case Value of
+ {bstring,String} ->
+ bstring_to_octetlist(String);
+ {hstring,String} ->
+ hstring_to_octetlist(String);
+ Rec when is_record(Rec,'Externalvaluereference') ->
+ get_normalized_value(S,Value,CType,
+ fun normalize_octetstring/3,[]);
+ {Name,String} when is_atom(Name) ->
+ normalize_octetstring(S,String,CType);
+ List when is_list(List) ->
+ %% check if list elements are valid octet values
+ lists:map(fun([])-> ok;
+ (H)when H > 255->
+ io:format("WARNING: not legal octet value ~p in OCTET STRING, ~p~n",[H,List]);
+ (_)-> ok
+ end, List),
+ List;
+ Other ->
+ io:format("WARNING: unknown default value ~p~n",[Other]),
+ Value
+ end.
+
+
+bstring_to_octetlist([]) ->
+ [];
+bstring_to_octetlist([H|T]) when H == $0 ; H == $1 ->
+ bstring_to_octetlist(T,6,[(H - $0) bsl 7]).
+bstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H == $0; H == $1 ->
+ bstring_to_octetlist(T, 7, [0,Hacc + (H -$0)| Tacc]);
+bstring_to_octetlist([H|T],BSL,[Hacc|Tacc]) when H == $0; H == $1 ->
+ bstring_to_octetlist(T, BSL-1, [Hacc + ((H - $0) bsl BSL)| Tacc]);
+bstring_to_octetlist([],7,[0|Acc]) ->
+ lists:reverse(Acc);
+bstring_to_octetlist([],_,Acc) ->
+ lists:reverse(Acc).
+
+hstring_to_octetlist([]) ->
+ [];
+hstring_to_octetlist(L) ->
+ hstring_to_octetlist(L,4,[]).
+hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $A, H =< $F ->
+ hstring_to_octetlist(T,4,[Hacc + (H - $A + 10)|Tacc]);
+hstring_to_octetlist([H|T],BSL,Acc) when H >= $A, H =< $F ->
+ hstring_to_octetlist(T,0,[(H - $A + 10) bsl BSL|Acc]);
+hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $0; H =< $9 ->
+ hstring_to_octetlist(T,4,[Hacc + (H - $0)|Tacc]);
+hstring_to_octetlist([H|T],BSL,Acc) when H >= $0; H =< $9 ->
+ hstring_to_octetlist(T,0,[(H - $0) bsl BSL|Acc]);
+hstring_to_octetlist([],_,Acc) ->
+ lists:reverse(Acc).
+
+normalize_objectidentifier(S,Value) ->
+ {ok,Val}=validate_objectidentifier(S,Value,[]),
+ Val.
+
+normalize_relative_oid(S,Value) ->
+ {ok,Val} = validate_relative_oid(S,Value,[]),
+ Val.
+
+normalize_objectdescriptor(Value) ->
+ Value.
+
+normalize_real(Value) ->
+ Value.
+
+normalize_enumerated(#'Externalvaluereference'{value=V},CType)
+ when is_list(CType) ->
+ normalize_enumerated2(V,CType);
+normalize_enumerated(Value,CType) when is_atom(Value),is_list(CType) ->
+ normalize_enumerated2(Value,CType);
+normalize_enumerated({Name,EnumV},CType) when is_atom(Name) ->
+ normalize_enumerated(EnumV,CType);
+normalize_enumerated(Value,{CType1,CType2}) when is_list(CType1), is_list(CType2)->
+ normalize_enumerated(Value,CType1++CType2);
+normalize_enumerated(V,CType) ->
+ io:format("WARNING: Enumerated unknown type ~p~n",[CType]),
+ V.
+normalize_enumerated2(V,Enum) ->
+ case lists:keysearch(V,1,Enum) of
+ {value,{Val,_}} -> Val;
+ _ ->
+ io:format("WARNING: Enumerated value is not correct ~p~n",[V]),
+ V
+ end.
+
+
+normalize_choice(S,{'CHOICE',{C,V}},CType,NameList) when is_atom(C) ->
+ case catch lists:keysearch(C,#'ComponentType'.name,CType) of
+ {value,#'ComponentType'{typespec=CT,name=Name}} ->
+ {C,normalize_value(S,CT,{'DEFAULT',V},
+ [Name|NameList])};
+ Other ->
+ io:format("WARNING: Wrong format of type/value ~p/~p~n",
+ [Other,V]),
+ {C,V}
+ end;
+normalize_choice(S,{'DEFAULT',ValueList},CType,NameList) when is_list(ValueList) ->
+ lists:map(fun(X)-> normalize_choice(S,X,CType,NameList) end, ValueList);
+normalize_choice(S,Val=#'Externalvaluereference'{},CType,NameList) ->
+ {M,#valuedef{value=V}}=get_referenced_type(S,Val),
+ normalize_choice(update_state(S,M),{'CHOICE',V},CType,NameList);
+% get_normalized_value(S,Val,CType,fun normalize_choice/4,[NameList]);
+normalize_choice(S,CV={Name,_ChoiceVal},CType,NameList)
+ when is_atom(Name) ->
+% normalize_choice(S,ChoiceVal,CType,NameList).
+ normalize_choice(S,{'CHOICE',CV},CType,NameList);
+normalize_choice(_S,V,_CType,_NameList) ->
+ exit({error,{bad_choice_value,V}}).
+
+%% normalize_choice(NameList,S,CVal = {'CHOICE',{_,_}},CType,_) ->
+%% normalize_choice(S,CVal,CType,NameList);
+%% normalize_choice(NameList,S,CVal={'DEFAULT',VL},CType,_) when is_list(VL)->
+%% normalize_choice(S,CVal,CType,NameList);
+%% normalize_choice(NameList,S,CV={Name,_CV},CType,_) when is_atom(Name)->
+%% normalize_choice(S,{'CHOICE',CV},CType,NameList);
+%% normalize_choice(_,_S,V,_,_) ->
+%% V.
+
+normalize_sequence(S,Value,Components,NameList)
+ when is_tuple(Components) ->
+ normalize_sequence(S,Value,lists:flatten(tuple_to_list(Components)),
+ NameList);
+normalize_sequence(S,{Name,Value},Components,NameList)
+ when is_atom(Name),is_list(Value) ->
+ normalize_sequence(S,Value,Components,NameList);
+normalize_sequence(S,Value,Components,NameList) ->
+ normalized_record('SEQUENCE',S,Value,Components,NameList).
+
+normalize_set(S,Value,Components,NameList) when is_tuple(Components) ->
+ normalize_set(S,Value,lists:flatten(tuple_to_list(Components)),NameList);
+normalize_set(S,{Name,Value},Components,NameList)
+ when is_atom(Name),is_list(Value) ->
+ normalized_record('SET',S,Value,Components,NameList);
+normalize_set(S,Value,Components,NameList) ->
+ NewName = list_to_atom(asn1ct_gen:list2name(NameList)),
+ case is_record_normalized(S,NewName,Value,length(Components)) of
+ true ->
+ Value;
+ _ ->
+ SortedVal = sort_value(Components,Value),
+ normalized_record('SET',S,SortedVal,Components,NameList)
+ end.
+
+sort_value(Components,Value) ->
+ ComponentNames = lists:map(fun(#'ComponentType'{name=Cname}) -> Cname end,
+ Components),
+ sort_value1(ComponentNames,Value,[]).
+sort_value1(_,V=#'Externalvaluereference'{},_) ->
+ %% sort later, get the value in normalize_seq_or_set
+ V;
+sort_value1([N|Ns],Value,Acc) ->
+ case lists:keysearch(N,1,Value) of
+ {value,V} ->sort_value1(Ns,Value,[V|Acc]);
+ _ -> sort_value1(Ns,Value,Acc)
+ end;
+sort_value1([],_,Acc) ->
+ lists:reverse(Acc).
+
+sort_val_if_set(['SET'|_],Val,Type) ->
+ sort_value(Type,Val);
+sort_val_if_set(_,Val,_) ->
+ Val.
+
+normalized_record(SorS,S,Value,Components,NameList) ->
+ NewName = list_to_atom(lists:concat([get_record_prefix_name(S),
+ asn1ct_gen:list2name(NameList)])),
+ case is_record_normalized(S,NewName,Value,length(Components)) of
+ true ->
+ Value;
+ _ ->
+ NoComps = length(Components),
+ case normalize_seq_or_set(SorS,S,Value,Components,NameList,[]) of
+ ListOfVals when length(ListOfVals) == NoComps ->
+ list_to_tuple([NewName|ListOfVals]);
+ _ ->
+ error({type,{illegal,default,value,Value},S})
+ end
+ end.
+is_record_normalized(S,Name,V = #'Externalvaluereference'{},NumComps) ->
+ case get_referenced_type(S,V) of
+ {_M,#valuedef{type=_T1,value=V2}} ->
+ is_record_normalized(S,Name,V2,NumComps);
+ _ -> false
+ end;
+is_record_normalized(_S,Name,Value,NumComps) when is_tuple(Value) ->
+ (size(Value) =:= (NumComps + 1)) andalso (element(1,Value)=:=Name);
+is_record_normalized(_,_,_,_) ->
+ false.
+
+normalize_seq_or_set(SorS,S,[{Cname,V}|Vs],
+ [#'ComponentType'{name=Cname,typespec=TS}|Cs],
+ NameList,Acc) ->
+ NewNameList =
+ case TS#type.def of
+ #'Externaltypereference'{type=TName} ->
+ [TName];
+ _ -> [Cname|NameList]
+ end,
+ NVal = normalize_value(S,TS,{'DEFAULT',V},NewNameList),
+ normalize_seq_or_set(SorS,S,Vs,Cs,NameList,[NVal|Acc]);
+normalize_seq_or_set(SorS,S,Values=[{_Cname1,_V}|_Vs],
+ [#'ComponentType'{prop='OPTIONAL'}|Cs],
+ NameList,Acc) ->
+ normalize_seq_or_set(SorS,S,Values,Cs,NameList,[asn1_NOVALUE|Acc]);
+normalize_seq_or_set(SorS,S,Values=[{_Cname1,_V}|_Vs],
+ [#'ComponentType'{name=Cname2,typespec=TS,
+ prop={'DEFAULT',Value}}|Cs],
+ NameList,Acc) ->
+ NewNameList =
+ case TS#type.def of
+ #'Externaltypereference'{type=TName} ->
+ [TName];
+ _ -> [Cname2|NameList]
+ end,
+ NVal = normalize_value(S,TS,{'DEFAULT',Value},NewNameList),
+ normalize_seq_or_set(SorS,S,Values,Cs,NameList,[NVal|Acc]);
+normalize_seq_or_set(_SorS,_S,[],[],_,Acc) ->
+ lists:reverse(Acc);
+%% If default value is {} ComponentTypes in SEQUENCE are marked DEFAULT
+%% or OPTIONAL (or the type is defined SEQUENCE{}, which is handled by
+%% the previous case).
+normalize_seq_or_set(SorS,S,[],
+ [#'ComponentType'{name=Name,typespec=TS,
+ prop={'DEFAULT',Value}}|Cs],
+ NameList,Acc) ->
+ NewNameList =
+ case TS#type.def of
+ #'Externaltypereference'{type=TName} ->
+ [TName];
+ _ -> [Name|NameList]
+ end,
+ NVal = normalize_value(S,TS,{'DEFAULT',Value},NewNameList),
+ normalize_seq_or_set(SorS,S,[],Cs,NameList,[NVal|Acc]);
+normalize_seq_or_set(SorS,S,[],[#'ComponentType'{prop='OPTIONAL'}|Cs],
+ NameList,Acc) ->
+ normalize_seq_or_set(SorS,S,[],Cs,NameList,[asn1_NOVALUE|Acc]);
+normalize_seq_or_set(SorS,S,Value=#'Externalvaluereference'{},
+ Cs,NameList,Acc) ->
+ get_normalized_value(S,Value,Cs,fun normalize_seq_or_set/6,
+ [SorS,NameList,Acc]);
+normalize_seq_or_set(_SorS,S,V,_,_,_) ->
+ error({type,{illegal,default,value,V},S}).
+
+normalize_seqof(S,Value,Type,NameList) ->
+ normalize_s_of('SEQUENCE OF',S,Value,Type,NameList).
+
+normalize_setof(S,Value,Type,NameList) ->
+ normalize_s_of('SET OF',S,Value,Type,NameList).
+
+normalize_s_of(SorS,S,Value,Type,NameList) when is_list(Value) ->
+ DefValueList = lists:map(fun(X) -> {'DEFAULT',X} end,Value),
+ Suffix = asn1ct_gen:constructed_suffix(SorS,Type),
+ Def = Type#type.def,
+ InnerType = asn1ct_gen:get_inner(Def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ NewNameList =
+ case WhatKind of
+ {constructed,bif} ->
+ [Suffix|NameList];
+ #'Externaltypereference'{type=Name} ->
+ [Name];
+ _ -> []
+ end,
+ NormFun = fun (X) -> normalize_value(S,Type,X,
+ NewNameList) end,
+ case catch lists:map(NormFun, DefValueList) of
+ List when is_list(List) ->
+ List;
+ _ ->
+ io:format("WARNING: ~p could not handle value ~p~n",
+ [SorS,Value]),
+ Value
+ end;
+normalize_s_of(SorS,S,Value,Type,NameList)
+ when is_record(Value,'Externalvaluereference') ->
+ get_normalized_value(S,Value,Type,fun normalize_s_of/5,
+ [SorS,NameList]).
+
+
+%% normalize_restrictedstring handles all format of restricted strings.
+%% tuple case
+% normalize_restrictedstring(_S,[Int1,Int2],_) when is_integer(Int1),is_integer(Int2) ->
+% {Int1,Int2};
+% %% quadruple case
+% normalize_restrictedstring(_S,[Int1,Int2,Int3,Int4],_) when is_integer(Int1),
+% is_integer(Int2),
+% is_integer(Int3),
+% is_integer(Int4) ->
+% {Int1,Int2,Int3,Int4};
+%% character string list case
+normalize_restrictedstring(S,[H|T],CType) when is_list(H);is_tuple(H) ->
+ [normalize_restrictedstring(S,H,CType)|normalize_restrictedstring(S,T,CType)];
+%% character sting case
+normalize_restrictedstring(_S,CString,_) when is_list(CString) ->
+ CString;
+%% definedvalue case or argument in a parameterized type
+normalize_restrictedstring(S,ERef,CType) when is_record(ERef,'Externalvaluereference') ->
+ get_normalized_value(S,ERef,CType,
+ fun normalize_restrictedstring/3,[]);
+%%
+normalize_restrictedstring(S,{Name,Val},CType) when is_atom(Name) ->
+ normalize_restrictedstring(S,Val,CType).
+
+normalize_objectclassfieldvalue(S,{opentypefieldvalue,Type,Value},NameList) ->
+ %% An open type has per definition no type. Thus should the type
+ %% information of the default type be available at
+ %% encode/decode. But as encoding the default value causes special
+ %% treatment (no encoding) whatever type is used the type
+ %% information is not necessary in encode/decode.
+ normalize_value(S,Type,Value,NameList);
+normalize_objectclassfieldvalue(_S,Other,_NameList) ->
+ %% If the type info was thrown away in an earlier step the value
+ %% is already normalized.
+ Other.
+
+get_normalized_value(S,Val,Type,Func,AddArg) ->
+ case catch get_referenced_type(S,Val) of
+ {ExtM,_VDef = #valuedef{type=_T1,value=V}} ->
+ %% should check that Type and T equals
+ V2 = sort_val_if_set(AddArg,V,Type),
+ call_Func(update_state(S,ExtM),V2,Type,Func,AddArg);
+ {error,_} ->
+ io:format("WARNING: default value not "
+ "comparable ~p~n",[Val]),
+ Val;
+ {ExtM,NewVal} ->
+ V2 = sort_val_if_set(AddArg,NewVal,Type),
+ call_Func(update_state(S,ExtM),V2,Type,Func,AddArg);
+ _ ->
+ io:format("WARNING: default value not "
+ "comparable ~p~n",[Val]),
+ Val
+ end.
+
+call_Func(S,Val,Type,Func,ArgList) ->
+ case ArgList of
+ [] ->
+ Func(S,Val,Type);
+ [LastArg] ->
+ Func(S,Val,Type,LastArg);
+ [Arg1,LastArg1] ->
+ Func(Arg1,S,Val,Type,LastArg1);
+ [Arg1,LastArg1,LastArg2] ->
+ Func(Arg1,S,Val,Type,LastArg1,LastArg2)
+ end.
+
+
+get_canonic_type(S,Type,NameList) ->
+ {InnerType,NewType,NewNameList} =
+ case Type#type.def of
+ Name when is_atom(Name) ->
+ {Name,Type,NameList};
+ Ref when is_record(Ref,'Externaltypereference') ->
+ {_,#typedef{name=Name,typespec=RefedType}} =
+ get_referenced_type(S,Ref),
+ get_canonic_type(S,RefedType,[Name]);
+ {Name,T} when is_atom(Name) ->
+ {Name,T,NameList};
+ Seq when is_record(Seq,'SEQUENCE') ->
+ {'SEQUENCE',Seq#'SEQUENCE'.components,NameList};
+ Set when is_record(Set,'SET') ->
+ {'SET',Set#'SET'.components,NameList};
+ #'ObjectClassFieldType'{type=T} ->
+ {'ASN1_OPEN_TYPE',T,NameList}
+ end,
+ {asn1ct_gen:unify_if_string(InnerType),NewType,NewNameList}.
+
+
+
+check_ptype(S,Type,Ts) when is_record(Ts,type) ->
+ %Tag = Ts#type.tag,
+ %Constr = Ts#type.constraint,
+ Def = Ts#type.def,
+ NewDef=
+ case Def of
+ Seq when is_record(Seq,'SEQUENCE') ->
+ Components = expand_components(S,Seq#'SEQUENCE'.components),
+ #newt{type=Seq#'SEQUENCE'{pname=get_datastr_name(Type),
+ components = Components}};
+ Set when is_record(Set,'SET') ->
+ Components = expand_components(S,Set#'SET'.components),
+ #newt{type=Set#'SET'{pname=get_datastr_name(Type),
+ components = Components}};
+ _Other ->
+ #newt{}
+ end,
+ Ts2 = case NewDef of
+ #newt{type=unchanged} ->
+ Ts;
+ #newt{type=TDef}->
+ Ts#type{def=TDef}
+ end,
+ Ts2;
+%parameterized class
+check_ptype(_S,_PTDef,Ts) when is_record(Ts,objectclass) ->
+ throw({asn1_param_class,Ts}).
+
+
+% check_type(S,Type,ObjSpec={{objectclassname,_},_}) ->
+% check_class(S,ObjSpec);
+check_type(_S,Type,Ts) when is_record(Type,typedef),
+ (Type#typedef.checked==true) ->
+ Ts;
+check_type(_S,Type,Ts) when is_record(Type,typedef),
+ (Type#typedef.checked==idle) -> % the check is going on
+ Ts;
+check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
+ {Def,Tag,Constr,IsInlined} =
+ case match_parameters(S,Ts#type.def,S#state.parameters) of
+ #type{tag=PTag,constraint=_Ctmp,def=Dtmp,inlined=Inl} ->
+ {Dtmp,merge_tags(Ts#type.tag,PTag),Ts#type.constraint,Inl};
+ #typedef{typespec=#type{tag=PTag,def=Dtmp,inlined=Inl}} ->
+ {Dtmp,merge_tags(Ts#type.tag,PTag),Ts#type.constraint,Inl};
+ Dtmp ->
+ {Dtmp,Ts#type.tag,Ts#type.constraint,Ts#type.inlined}
+ end,
+ TempNewDef = #newt{type=Def,tag=Tag,constraint=Constr,
+ inlined=IsInlined},
+ TestFun =
+ fun(Tref) ->
+ {_,MaybeChoice} = get_referenced_type(S,Tref),
+ case catch((MaybeChoice#typedef.typespec)#type.def) of
+ {'CHOICE',_} ->
+ maybe_illicit_implicit_tag(choice,Tag);
+ 'ANY' ->
+ maybe_illicit_implicit_tag(open_type,Tag);
+ 'ANY DEFINED BY' ->
+ maybe_illicit_implicit_tag(open_type,Tag);
+ 'ASN1_OPEN_TYPE' ->
+ maybe_illicit_implicit_tag(open_type,Tag);
+ _ ->
+ Tag
+ end
+ end,
+ NewDef=
+ case Def of
+ Ext when is_record(Ext,'Externaltypereference') ->
+ {RefMod,RefTypeDef,IsParamDef} =
+ case get_referenced_type(S,Ext) of
+ {undefined,TmpTDef} -> %% A parameter
+ {get(top_module),TmpTDef,true};
+ {TmpRefMod,TmpRefDef} ->
+ {TmpRefMod,TmpRefDef,false}
+ end,
+ case is_class(S,RefTypeDef) of
+ true -> throw({asn1_class,RefTypeDef});
+ _ -> ok
+ end,
+ Ct = TestFun(Ext),
+ {RefType,ExtRef} =
+ case RefTypeDef#typedef.checked of
+ true ->
+ {RefTypeDef#typedef.typespec,Ext};
+ _ ->
+ %% Put as idle to prevent recursive loops
+ NewRefTypeDef1 = RefTypeDef#typedef{checked=idle},
+ asn1_db:dbput(RefMod,
+ get_datastr_name(NewRefTypeDef1),
+ NewRefTypeDef1),
+ NewS = S#state{mname=RefMod,
+ module=load_asn1_module(S,RefMod),
+ tname=get_datastr_name(NewRefTypeDef1),
+ type=NewRefTypeDef1,
+ abscomppath=[],recordtopname=[]},
+ RefType1 =
+ check_type(NewS,RefTypeDef,RefTypeDef#typedef.typespec),
+ %% update the type and mark as checked
+ NewRefTypeDef2 =
+ RefTypeDef#typedef{checked=true,typespec = RefType1},
+ TmpName = get_datastr_name(NewRefTypeDef2),
+ asn1_db:dbput(RefMod,
+ TmpName,
+ NewRefTypeDef2),
+ case {RefMod == get(top_module),IsParamDef} of
+ {true,true} ->
+ Key = {TmpName,
+ type,
+ NewRefTypeDef2},
+ asn1ct_gen:insert_once(parameterized_objects,
+ Key);
+ _ -> ok
+ end,
+ {RefType1,#'Externaltypereference'{module=RefMod,
+ type=TmpName}}
+ end,
+
+ case asn1ct_gen:prim_bif(asn1ct_gen:get_inner(RefType#type.def)) of
+ true ->
+ %% Here we expand to a built in type and inline it
+ NewS2 = S#state{type=#typedef{typespec=RefType}},
+ NewC =
+ constraint_merge(NewS2,
+ check_constraints(NewS2,Constr)++
+ RefType#type.constraint),
+ TempNewDef#newt{
+ type = RefType#type.def,
+ tag = merge_tags(Ct,RefType#type.tag),
+ constraint = NewC};
+ _ ->
+ %% Here we only expand the tags and keep the ext ref.
+
+ NewExt = ExtRef#'Externaltypereference'{module=merged_mod(S,RefMod,Ext)},
+ TempNewDef#newt{
+ type = check_externaltypereference(S,NewExt),
+ tag = case S#state.erule of
+ ber_bin_v2 ->
+ merge_tags(Ct,RefType#type.tag);
+ _ ->
+ Ct
+ end
+ }
+ end;
+ 'ANY' ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+ {'ANY_DEFINED_BY',_} ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+ 'INTEGER' ->
+ check_integer(S,[],Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
+
+ {'INTEGER',NamedNumberList} ->
+ TempNewDef#newt{type={'INTEGER',check_integer(S,NamedNumberList,Constr)},
+ tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
+ 'REAL' ->
+ check_real(S,Constr),
+ TempNewDef#newt{tag=merge_tags(Tag,?TAG_PRIMITIVE(?N_REAL))};
+ {'BIT STRING',NamedNumberList} ->
+ NewL = check_bitstring(S,NamedNumberList,Constr),
+%% erlang:display({asn1ct_check,NamedNumberList,NewL}),
+ TempNewDef#newt{type={'BIT STRING',NewL},
+ tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_BIT_STRING))};
+ 'NULL' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_NULL))};
+ 'OBJECT IDENTIFIER' ->
+ check_objectidentifier(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_OBJECT_IDENTIFIER))};
+ 'ObjectDescriptor' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_OBJECT_DESCRIPTOR))};
+ 'EXTERNAL' ->
+ put_once(external,unchecked),
+ TempNewDef#newt{type=
+ #'Externaltypereference'{module=S#state.mname,
+ type='EXTERNAL'},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_EXTERNAL))};
+ {'INSTANCE OF',DefinedObjectClass,Constraint} ->
+ %% check that DefinedObjectClass is of TYPE-IDENTIFIER class
+ %% If Constraint is empty make it the general INSTANCE OF type
+ %% If Constraint is not empty make an inlined type
+ %% convert INSTANCE OF to the associated type
+ IOFDef=check_instance_of(S,DefinedObjectClass,Constraint),
+ TempNewDef#newt{type=IOFDef,
+ tag=merge_tags(Tag,?TAG_CONSTRUCTED(?N_INSTANCE_OF))};
+ {'ENUMERATED',NamedNumberList} ->
+ TempNewDef#newt{type=
+ {'ENUMERATED',
+ check_enumerated(S,NamedNumberList,Constr)},
+ tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_ENUMERATED)),
+ constraint=[]};
+ 'EMBEDDED PDV' ->
+ put_once(embedded_pdv,unchecked),
+ TempNewDef#newt{type=
+ #'Externaltypereference'{module=S#state.mname,
+ type='EMBEDDED PDV'},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_EMBEDDED_PDV))};
+ 'BOOLEAN'->
+ check_boolean(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_BOOLEAN))};
+ 'OCTET STRING' ->
+ check_octetstring(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_OCTET_STRING))};
+ 'NumericString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_NumericString))};
+ TString when TString =:= 'TeletexString';
+ TString =:= 'T61String' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_TeletexString))};
+ 'VideotexString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_VideotexString))};
+ 'UTCTime' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_UTCTime))};
+ 'GeneralizedTime' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_GeneralizedTime))};
+ 'GraphicString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_GraphicString))};
+ 'VisibleString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_VisibleString))};
+ 'GeneralString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_GeneralString))};
+ 'PrintableString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_PrintableString))};
+ 'IA5String' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_IA5String))};
+ 'BMPString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_BMPString))};
+ 'UniversalString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_UniversalString))};
+ 'UTF8String' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_UTF8String))};
+ 'RELATIVE-OID' ->
+ check_relative_oid(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?'N_RELATIVE-OID'))};
+ 'CHARACTER STRING' ->
+ put_once(character_string,unchecked),
+ TempNewDef#newt{type=
+ #'Externaltypereference'{module=S#state.mname,
+ type='CHARACTER STRING'},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_CHARACTER_STRING))};
+ Seq when is_record(Seq,'SEQUENCE') ->
+ RecordName =
+ case TopName of
+ [] ->
+ [get_datastr_name(Type)];
+% [Type#typedef.name];
+ _ ->
+ TopName
+ end,
+ {TableCInf,Components} =
+ check_sequence(S#state{recordtopname=
+ RecordName},
+ Type,Seq#'SEQUENCE'.components),
+ TempNewDef#newt{type=Seq#'SEQUENCE'{tablecinf=tablecinf_choose(Seq,TableCInf),
+ components=Components},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SEQUENCE))};
+ {'SEQUENCE OF',Components} ->
+ TempNewDef#newt{type={'SEQUENCE OF',check_sequenceof(S,Type,Components)},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SEQUENCE))};
+ {'CHOICE',Components} ->
+ Ct = maybe_illicit_implicit_tag(choice,Tag),
+ TempNewDef#newt{type={'CHOICE',check_choice(S,Type,Components)},tag=Ct};
+ Set when is_record(Set,'SET') ->
+ RecordName=
+ case TopName of
+ [] ->
+ [get_datastr_name(Type)];
+% [Type#typedef.name];
+ _ ->
+ TopName
+ end,
+ {Sorted,TableCInf,Components} =
+ check_set(S#state{recordtopname=RecordName},
+ Type,Set#'SET'.components),
+ TempNewDef#newt{type=Set#'SET'{sorted=Sorted,
+ tablecinf=tablecinf_choose(Set,TableCInf),
+ components=Components},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SET))};
+ {'SET OF',Components} ->
+ TempNewDef#newt{type={'SET OF',check_setof(S,Type,Components)},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SET))};
+ %% This is a temporary hack until the full Information Obj Spec
+ %% in X.681 is supported
+ {{typereference,_,'TYPE-IDENTIFIER'},[{typefieldreference,_,'Type'}]} ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+
+ {#'Externaltypereference'{type='TYPE-IDENTIFIER'},
+ [{typefieldreference,_,'Type'}]} ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+
+ {pt,Ptype,ParaList} ->
+ %% Ptype might be a parameterized - type, object set or
+ %% value set. If it isn't a parameterized type notify the
+ %% calling function.
+ {_RefMod,Ptypedef} = get_referenced_type(S,Ptype),
+ notify_if_not_ptype(S,Ptypedef),
+ NewParaList =
+ [match_parameters(S,TmpParam,S#state.parameters)||
+ TmpParam <- ParaList],
+ Instance = instantiate_ptype(S,Ptypedef,NewParaList),
+ TempNewDef#newt{type=Instance#type.def,
+ tag=merge_tags(Tag,Instance#type.tag),
+ constraint=Instance#type.constraint,
+ inlined=yes};
+
+ OCFT=#'ObjectClassFieldType'{classname=ClRef} ->
+ %% this case occures in a SEQUENCE when
+ %% the type of the component is a ObjectClassFieldType
+ ClassSpec = check_class(S,ClRef),
+ NewTypeDef =
+ maybe_open_type(S,ClassSpec,
+ OCFT#'ObjectClassFieldType'{class=ClassSpec},Constr),
+ InnerTag = get_innertag(S,NewTypeDef),
+ MergedTag = merge_tags(Tag,InnerTag),
+ Ct =
+ case is_open_type(NewTypeDef) of
+ true ->
+ maybe_illicit_implicit_tag(open_type,MergedTag);
+ _ ->
+ MergedTag
+ end,
+ TempNewDef#newt{type=NewTypeDef,tag=Ct};
+
+ {'TypeFromObject',{object,Object},TypeField} ->
+ CheckedT = get_type_from_object(S,Object,TypeField),
+ TempNewDef#newt{tag=merge_tags(Tag,CheckedT#type.tag),
+ type=CheckedT#type.def};
+
+ {valueset,Vtype} ->
+ TempNewDef#newt{type={valueset,check_type(S,Type,Vtype)}};
+ {'SelectionType',Name,T} ->
+ CheckedT = check_selectiontype(S,Name,T),
+ TempNewDef#newt{tag=merge_tags(Tag,CheckedT#type.tag),
+ type=CheckedT#type.def};
+ Other ->
+ exit({'cant check' ,Other})
+ end,
+ Ts2 = case NewDef of
+ #newt{type=unchanged} ->
+ Ts#type{def=Def};
+ #newt{type=TDef}->
+ Ts#type{def=TDef}
+ end,
+ NewTag = case NewDef of
+ #newt{tag=unchanged} ->
+ Tag;
+ #newt{tag=TT} ->
+ TT
+ end,
+ T3 = Ts2#type{tag = lists:map(fun(TempTag = #tag{type={default,TTx}}) ->
+ TempTag#tag{type=TTx};
+ (Else) -> Else end, NewTag)},
+ T4 = case NewDef of
+ #newt{constraint=unchanged} ->
+ T3#type{constraint=Constr};
+ #newt{constraint=NewConstr} ->
+ T3#type{constraint=NewConstr}
+ end,
+ T5 = T4#type{inlined=NewDef#newt.inlined},
+ T5#type{constraint=check_constraints(S,T5#type.constraint)};
+check_type(_S,Type,Ts) ->
+ exit({error,{asn1,internal_error,Type,Ts}}).
+
+%% tablecinf_choose. A SEQUENCE or SET may be inserted in another
+%% SEQUENCE or SET by the COMPONENTS OF directive. If this inserted
+%% type is a referenced type that already has been checked it already
+%% has its tableconstraint information. Furthermore this information
+%% may be lost in the analysis in the new environment. Assume this
+%% SEQUENCE/SET has a simpletable constraint and a componentrelation
+%% constraint whose atlist points to the outermost component of its
+%% "standalone" definition. This will cause the analysis to fail as it
+%% will not find the right atlist component in the outermost
+%% environment in the new inlined environment.
+tablecinf_choose(SetOrSeq,false) ->
+ tablecinf_choose(SetOrSeq);
+tablecinf_choose(_, TableCInf) ->
+ TableCInf.
+tablecinf_choose(#'SET'{tablecinf=TCI}) ->
+ TCI;
+tablecinf_choose(#'SEQUENCE'{tablecinf=TCI}) ->
+ TCI.
+
+get_innertag(_S,#'ObjectClassFieldType'{type=Type}) ->
+ case Type of
+% #type{tag=Tag} -> Tag;
+% {fixedtypevaluefield,_,#type{tag=[]}=T} -> get_taglist(S,T);
+ {fixedtypevaluefield,_,#type{tag=Tag}} -> Tag;
+ {TypeFieldName,_} when is_atom(TypeFieldName) -> [];
+ _ -> []
+ end.
+
+get_type_from_object(S,Object,TypeField)
+ when is_record(Object,'Externaltypereference');
+ is_record(Object,'Externalvaluereference') ->
+ {_,ObjectDef} = get_referenced_type(S,Object),
+ ObjSpec = check_object(S,ObjectDef,ObjectDef#typedef.typespec),
+ get_fieldname_element(S,ObjectDef#typedef{typespec=ObjSpec},TypeField).
+
+is_class(_S,#classdef{}) ->
+ true;
+is_class(S,#typedef{typespec=#type{def=Eref}})
+ when is_record(Eref,'Externaltypereference')->
+ is_class(S,Eref);
+is_class(S,Eref) when is_record(Eref,'Externaltypereference')->
+ {_,NextDef} = get_referenced_type(S,Eref),
+ is_class(S,NextDef);
+is_class(_,_) ->
+ false.
+
+get_class_def(_S,CD=#classdef{}) ->
+ CD;
+get_class_def(S,#typedef{typespec=#type{def=Eref}})
+ when is_record(Eref,'Externaltypereference') ->
+ {_,NextDef} = get_referenced_type(S,Eref),
+ get_class_def(S,NextDef).
+
+maybe_illicit_implicit_tag(Kind,Tag) ->
+ case Tag of
+ [#tag{type='IMPLICIT'}|_T] ->
+ throw({error,{asn1,{implicit_tag_before,Kind}}});
+ [ChTag = #tag{type={default,_}}|T] ->
+ case Kind of
+ open_type ->
+ [ChTag#tag{type='EXPLICIT',form=32}|T]; %X.680 30.6c, X.690 8.14.2
+ choice ->
+ [ChTag#tag{type='EXPLICIT',form=32}|T] % X.680 28.6 c, 30.6c
+ end;
+ _ ->
+ Tag % unchanged
+ end.
+
+
+merged_mod(S,RefMod,Ext) ->
+ case S of
+ #state{inputmodules=[]} ->
+ RefMod;
+ _ ->
+ Ext#'Externaltypereference'.module
+ end.
+
+%% maybe_open_type/2 -> #ObjectClassFieldType with updated fieldname and
+%% type
+%% if the FieldRefList points out a typefield and the class don't have
+%% any UNIQUE field, so that a component relation constraint cannot specify
+%% the type of a typefield, return 'ASN1_OPEN_TYPE'.
+%%
+maybe_open_type(S,ClassSpec=#objectclass{fields=Fs},
+ OCFT=#'ObjectClassFieldType'{fieldname=FieldRefList},
+ Constr) ->
+ Type = get_ObjectClassFieldType(S,Fs,FieldRefList),
+ FieldNames=get_referenced_fieldname(FieldRefList),
+ case last_fieldname(FieldRefList) of
+ {valuefieldreference,_} ->
+ OCFT#'ObjectClassFieldType'{fieldname=FieldNames,
+ type=Type};
+ {typefieldreference,_} ->
+ case {catch get_unique_fieldname(S,#classdef{typespec=ClassSpec}),
+ asn1ct_gen:get_constraint(Constr,componentrelation)}of
+ {Tuple,_} when is_tuple(Tuple), size(Tuple) =:= 3 ->
+ OCFT#'ObjectClassFieldType'{fieldname=FieldNames,
+ type='ASN1_OPEN_TYPE'};
+ {_,no} ->
+ OCFT#'ObjectClassFieldType'{fieldname=FieldNames,
+ type='ASN1_OPEN_TYPE'};
+ _ ->
+ OCFT#'ObjectClassFieldType'{fieldname=FieldNames,
+ type=Type}
+ end
+ end.
+
+last_fieldname(FieldRefList) when is_list(FieldRefList) ->
+ lists:last(FieldRefList);
+last_fieldname({FieldName,_}) when is_atom(FieldName) ->
+ [A|_] = atom_to_list(FieldName),
+ case is_lowercase(A) of
+ true ->
+ {valuefieldreference,FieldName};
+ _ ->
+ {typefieldreference,FieldName}
+ end.
+
+is_open_type(#'ObjectClassFieldType'{type='ASN1_OPEN_TYPE'}) ->
+ true;
+is_open_type(#'ObjectClassFieldType'{}) ->
+ false.
+
+
+notify_if_not_ptype(S,#pvaluesetdef{type=Type}) ->
+ case Type#type.def of
+ Ref when is_record(Ref,'Externaltypereference') ->
+ case get_referenced_type(S,Ref) of
+ {_,#classdef{}} ->
+ throw(pobjectsetdef);
+ {_,#typedef{}} ->
+ throw(pvalueset)
+ end;
+ T when is_record(T,type) -> % this must be a value set
+ throw(pvalueset)
+ end;
+notify_if_not_ptype(_S,PT=#ptypedef{}) ->
+ %% this may be a parameterized CLASS, in that case throw an
+ %% asn1_class exception
+ case PT#ptypedef.typespec of
+ #objectclass{} -> throw({asn1_class,PT});
+ _ -> ok
+ end;
+notify_if_not_ptype(S,#pobjectsetdef{class=Cl}) ->
+ case Cl of
+ #'Externaltypereference'{} ->
+ case get_referenced_type(S,Cl) of
+ {_,#classdef{}} ->
+ throw(pobjectsetdef);
+ {_,#typedef{}} ->
+ throw(pvalueset)
+ end;
+ _ ->
+ throw(pobjectsetdef)
+ end;
+notify_if_not_ptype(_S,PT) ->
+ throw({error,{"supposed to be a parameterized type",PT}}).
+% fix me
+instantiate_ptype(S,Ptypedef,ParaList) ->
+ #ptypedef{args=Args,typespec=Type} = Ptypedef,
+ NewType = check_ptype(S,Ptypedef,Type#type{inlined=yes}),
+ MatchedArgs = match_args(S,Args, ParaList, []),
+ OldArgs = S#state.parameters,
+ NewS = S#state{type=NewType,parameters=MatchedArgs++OldArgs,abscomppath=[]},
+%% NewS = S#state{type=NewType,parameters=MatchedArgs,abscomppath=[]},
+ check_type(NewS, Ptypedef#ptypedef{typespec=NewType}, NewType).
+
+get_datastr_name(#typedef{name=N}) ->
+ N;
+get_datastr_name(#classdef{name=N}) ->
+ N;
+get_datastr_name(#valuedef{name=N}) ->
+ N;
+get_datastr_name(#ptypedef{name=N}) ->
+ N;
+get_datastr_name(#pvaluedef{name=N}) ->
+ N;
+get_datastr_name(#pvaluesetdef{name=N}) ->
+ N;
+get_datastr_name(#pobjectdef{name=N}) ->
+ N;
+get_datastr_name(#pobjectsetdef{name=N}) ->
+ N.
+
+
+get_pt_args(#ptypedef{args=Args}) ->
+ Args;
+get_pt_args(#pvaluesetdef{args=Args}) ->
+ Args;
+get_pt_args(#pvaluedef{args=Args}) ->
+ Args;
+get_pt_args(#pobjectdef{args=Args}) ->
+ Args;
+get_pt_args(#pobjectsetdef{args=Args}) ->
+ Args.
+
+get_pt_spec(#ptypedef{typespec=Type}) ->
+ Type;
+get_pt_spec(#pvaluedef{value=Value}) ->
+ Value;
+get_pt_spec(#pvaluesetdef{valueset=VS}) ->
+ VS;
+get_pt_spec(#pobjectdef{def=Def}) ->
+ Def;
+get_pt_spec(#pobjectsetdef{def=Def}) ->
+ Def.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% match_args(S,FormalArgs, ActualArgs, Accumulator) -> Result
+%% S = #state{}
+%% FormalArgs = [term()] | [{Governor,Parameter}]
+%% ActualArgs = [term()]
+%% Accumulator = [term()]
+%% Result = [{term(),term()}] | throw()
+%% Governor = #type{} | Reference | 'TYPE-IDENTIFIER' | 'ABSTRACT-SYNTAX'
+%% Parameter = Reference | {Governor,Reference}
+%% Reference = #'Externaltypereference'{} | #'Externalvaluerference'{}
+%%
+%% Different categories of parameters and governors (Dubuisson p.382)
+%% +----------------+-------------------------------+----------------------+
+%% |Governor is | Parameter name style | Parameter is |
+%% +----------------+-------------------------------+----------------------+
+%% | absent | begins with uppercase,(bu) | a type |
+%% | | | |
+%% | a type | begins with a lowercase,(bl)| a value |
+%% | | | |
+%% | a type | begins with an uppercase | a value set |
+%% | | | |
+%% | absent | entirely in uppercase, (eu) | a class (or type) |
+%% | | | |
+%% | a class name | begins with a lowercase | an object |
+%% | | | |
+%% | a class name | begins with an uppercase | an object set |
+%% +----------------+-------------------------------+----------------------+
+%%
+%% Matches each of the formal parameters to corresponding actual
+%% parameter, and changes format of the actual parameter according to
+%% above table if necessary.
+match_args(S,FA = [FormArg|Ft], AA = [ActArg|At], Acc) ->
+ OldParams = S#state.parameters,
+ case categorize_arg(S,FormArg,ActArg) of
+ [CategorizedArg] ->
+ match_args(S#state{parameters=
+ [{FormArg,CategorizedArg}|OldParams]},
+ Ft, At, [{FormArg,CategorizedArg}|Acc]);
+ CategorizedArgs ->
+ match_args(S#state{parameters=CategorizedArgs++OldParams},
+ FA, CategorizedArgs ++ AA, Acc)
+ end;
+match_args(_S,[], [], Acc) ->
+ lists:reverse(Acc);
+match_args(_,_, _, _) ->
+ throw({error,{asn1,{wrong_number_of_arguments}}}).
+
+%%%%%%%%%%%%%%%%%
+%% categorize_arg(S,FormalArg,ActualArg) -> {FormalArg,CatgorizedActualArg}
+%%
+categorize_arg(S,{Governor,Param},ActArg) ->
+ case {governor_category(S,Governor),parameter_name_style(Param,ActArg)} of
+%% {absent,beginning_uppercase} -> %% a type
+%% categorize(S,type,ActArg);
+ {type,beginning_lowercase} -> %% a value
+ categorize(S,value,Governor,ActArg);
+ {type,beginning_uppercase} -> %% a value set
+ categorize(S,value_set,ActArg);
+%% {absent,entirely_uppercase} -> %% a class
+%% categorize(S,class,ActArg);
+ {{class,ClassRef},beginning_lowercase} ->
+ categorize(S,object,ActArg,ClassRef);
+ {{class,ClassRef},beginning_uppercase} ->
+ categorize(S,object_set,ActArg,ClassRef);
+ _ ->
+ [ActArg]
+ end;
+categorize_arg(S,FormalArg,ActualArg) ->
+ %% governor is absent => a type or a class
+ case FormalArg of
+ #'Externaltypereference'{type=Name} ->
+ case is_class_name(Name) of
+ true ->
+ categorize(S,class,ActualArg);
+ _ ->
+ categorize(S,type,ActualArg)
+ end;
+ FA ->
+ throw({error,{unexpected_formal_argument,FA}})
+ end.
+
+governor_category(S,#type{def=Eref})
+ when is_record(Eref,'Externaltypereference') ->
+ governor_category(S,Eref);
+governor_category(_S,#type{}) ->
+ type;
+governor_category(S,Ref) when is_record(Ref,'Externaltypereference') ->
+ case is_class(S,Ref) of
+ true ->
+ {class,Ref};
+ _ ->
+ type
+ end;
+governor_category(_,Class)
+ when Class == 'TYPE-IDENTIFIER'; Class == 'ABSTRACT-SYNTAX' ->
+ class.
+%% governor_category(_,_) ->
+%% absent.
+
+%% parameter_name_style(Param,Data) -> Result
+%% gets the Parameter and the name of the Data and if it exists tells
+%% whether it begins with a lowercase letter or is partly or entirely
+%% spelled with uppercase letters. Otherwise returns undefined
+%%
+parameter_name_style(_,#'Externaltypereference'{type=Name}) ->
+ name_category(Name);
+parameter_name_style(_,#'Externalvaluereference'{value=Name}) ->
+ name_category(Name);
+parameter_name_style(_,{valueset,_}) ->
+ %% It is a object set or value set
+ beginning_uppercase;
+parameter_name_style(#'Externalvaluereference'{},_) ->
+ beginning_lowercase;
+parameter_name_style(#'Externaltypereference'{type=Name},_) ->
+ name_category(Name);
+parameter_name_style(_,_) ->
+ undefined.
+
+name_category(Atom) when is_atom(Atom) ->
+ name_category(atom_to_list(Atom));
+name_category([H|T]) ->
+ case is_lowercase(H) of
+ true ->
+ beginning_lowercase;
+ _ ->
+ case is_class_name(T) of
+ true ->
+ entirely_uppercase;
+ _ ->
+ beginning_uppercase
+ end
+ end;
+name_category(_) ->
+ undefined.
+
+is_lowercase(X) when X >= $A,X =< $W ->
+ false;
+is_lowercase(_) ->
+ true.
+
+is_class_name(Name) when is_atom(Name) ->
+ is_class_name(atom_to_list(Name));
+is_class_name(Name) ->
+ case [X||X <- Name, X >= $a,X =< $w] of
+ [] ->
+ true;
+ _ ->
+ false
+ end.
+
+%% categorize(S,Category,Parameter) -> CategorizedParameter
+%% If Parameter has an abstract syntax of another category than
+%% Category, transform it to a known syntax.
+categorize(_S,type,{object,_,Type}) ->
+ %% One example of this case is an object with a parameterized type
+ %% having a locally defined type as parameter.
+ Def = fun(D = #type{}) ->
+ #typedef{name = new_reference_name("type_argument"),
+ typespec = D#type{inlined=yes}};
+ ({setting,_,Eref}) when is_record(Eref,'Externaltypereference') ->
+ Eref;
+ (D) ->
+ D
+ end,
+ [Def(X)||X<-Type];
+categorize(_S,type,Def) when is_record(Def,type) ->
+ [#typedef{name = new_reference_name("type_argument"),
+ typespec = Def#type{inlined=yes}}];
+categorize(_,_,Def) ->
+ [Def].
+categorize(S,object_set,Def,ClassRef) ->
+ %% XXXXXXXXXX
+ case Def of
+ {'Externaltypereference',undefined,'MSAccessProtocol','AllOperations'} ->
+ ok;
+ _ ->
+ ok
+ end,
+ NewObjSetSpec =
+ check_object(S,Def,#'ObjectSet'{class = ClassRef,
+ set = parse_objectset(Def)}),
+ Name = new_reference_name("object_set_argument"),
+ %% XXXXXXXXXX
+ case Name of
+ internal_object_set_argument_78 ->
+ ok;
+ internal_object_set_argument_77 ->
+ ok;
+ _ ->
+ ok
+ end,
+ [save_object_set_instance(S,Name,NewObjSetSpec)];
+categorize(_S,object,Def,_ClassRef) ->
+ %% should be handled
+ [Def];
+categorize(_S,value,_Type,Value) when is_record(Value,valuedef) ->
+ [Value];
+categorize(S,value,Type,Value) ->
+%% [check_value(S,#valuedef{type=Type,value=Value})].
+ [#valuedef{type=Type,value=Value,module=S#state.mname}].
+
+
+parse_objectset({valueset,T=#type{}}) ->
+ [T];
+parse_objectset({valueset,Set}) ->
+ Set;
+parse_objectset(#type{def=Ref}) when is_record(Ref,'Externaltypereference') ->
+ Ref;
+parse_objectset(Set) ->
+ %% extend this later
+ Set.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% check_constraints/2
+%%
+check_constraints(S,C) when is_list(C) ->
+ check_constraints(S, C, []);
+check_constraints(S,C) when is_record(C,constraint) ->
+ check_constraints(S, C#constraint.c, []).
+
+resolv_tuple_or_list(S,List) when is_list(List) ->
+ lists:map(fun(X)->resolv_value(S,X) end, List);
+resolv_tuple_or_list(S,{Lb,Ub}) ->
+ {resolv_value(S,Lb),resolv_value(S,Ub)}.
+
+%%%-----------------------------------------
+%% If the constraint value is a defined value the valuename
+%% is replaced by the actual value
+%%
+resolv_value(S,Val) ->
+ Id = match_parameters(S,Val, S#state.parameters),
+ resolv_value1(S,Id).
+
+resolv_value1(S, ERef = #'Externalvaluereference'{value=Name}) ->
+ case catch resolve_namednumber(S,S#state.type,Name) of
+ V when is_integer(V) -> V;
+ _ ->
+ case get_referenced_type(S,ERef) of
+ {Err,_Reason} when Err == error; Err == 'EXIT' ->
+ throw({error,{asn1,{undefined_type_or_value,
+ Name}}});
+ {_M,VDef} ->
+ resolv_value1(S,VDef)
+ end
+ end;
+resolv_value1(S,{gt,V}) ->
+ case V of
+ Int when is_integer(Int) ->
+ V + 1;
+ #valuedef{value=Int} ->
+ 1 + resolv_value(S,Int);
+ Other ->
+ throw({error,{asn1,{undefined_type_or_value,Other}}})
+ end;
+resolv_value1(S,{lt,V}) ->
+ case V of
+ Int when is_integer(Int) ->
+ V - 1;
+ #valuedef{value=Int} ->
+ resolv_value(S,Int) - 1;
+ Other ->
+ throw({error,{asn1,{undefined_type_or_value,Other}}})
+ end;
+resolv_value1(S,{'ValueFromObject',{object,Object},[{valuefieldreference,
+ FieldName}]}) ->
+ %% FieldName can hold either a fixed-type value or a variable-type value
+ %% Object is a DefinedObject, i.e. a #'Externaltypereference'
+ resolve_value_from_object(S,Object,FieldName);
+resolv_value1(_,#valuedef{checked=true,value=V}) ->
+ V;
+resolv_value1(S,#valuedef{type=_T,
+ value={'ValueFromObject',{object,Object},
+ [{valuefieldreference,
+ FieldName}]}}) ->
+ resolve_value_from_object(S,Object,FieldName);
+resolv_value1(S,VDef = #valuedef{}) ->
+ #valuedef{value=Val} = check_value(S,VDef),
+ Val;
+resolv_value1(_,V) ->
+ V.
+resolve_value_from_object(S,Object,FieldName) ->
+ {_,ObjTDef} = get_referenced_type(S,Object),
+ TS = check_object(S,ObjTDef,ObjTDef#typedef.typespec),
+ {_,_,Components} = TS#'Object'.def,
+ case lists:keysearch(FieldName,1,Components) of
+ {value,{_,#valuedef{value=Val}}} ->
+ Val;
+ _ ->
+ error({value,"illegal value in constraint",S})
+ end.
+
+
+
+resolve_namednumber(S,#typedef{typespec=Type},Name) ->
+ case Type#type.def of
+ {'ENUMERATED',NameList} ->
+ NamedNumberList=check_enumerated(S,NameList,Type#type.constraint),
+ N = normalize_enumerated(Name,NamedNumberList),
+ {value,{_,V}} = lists:keysearch(N,1,NamedNumberList),
+ V;
+ {'INTEGER',NameList} ->
+ NamedNumberList = check_enumerated(S,NameList,Type#type.constraint),
+ {value,{_,V}} = lists:keysearch(Name,1,NamedNumberList),
+ V;
+ _ ->
+ not_enumerated
+ end.
+
+check_constraints(S,[{'ContainedSubtype',Type} | Rest], Acc) ->
+ {RefMod,CTDef} = get_referenced_type(S,Type#type.def),
+ NewS = S#state{module=load_asn1_module(S,RefMod),mname=RefMod,
+ type=CTDef,tname=get_datastr_name(CTDef)},
+ CType = check_type(NewS,S#state.tname,CTDef#typedef.typespec),
+ check_constraints(S,Rest,CType#type.constraint ++ Acc);
+check_constraints(S,[C | Rest], Acc) ->
+ check_constraints(S,Rest,[check_constraint(S,C) | Acc]);
+check_constraints(S,[],Acc) ->
+ constraint_merge(S,Acc).
+
+
+range_check(F={FixV,FixV}) ->
+% FixV;
+ F;
+range_check(VR={Lb,Ub}) when Lb < Ub ->
+ VR;
+range_check(Err={_,_}) ->
+ throw({error,{asn1,{illegal_size_constraint,Err}}});
+range_check(Value) ->
+ Value.
+
+check_constraint(S,Ext) when is_record(Ext,'Externaltypereference') ->
+ check_externaltypereference(S,Ext);
+
+
+check_constraint(S,{'SizeConstraint',{Lb,Ub}})
+ when is_list(Lb);is_tuple(Lb),size(Lb)==2 ->
+ NewLb = range_check(resolv_tuple_or_list(S,Lb)),
+ NewUb = range_check(resolv_tuple_or_list(S,Ub)),
+ {'SizeConstraint',{NewLb,NewUb}};
+check_constraint(S,{'SizeConstraint',{Lb,Ub}}) ->
+ case {resolv_value(S,Lb),resolv_value(S,Ub)} of
+ {FixV,FixV} ->
+ {'SizeConstraint',FixV};
+ {Low,High} when Low < High ->
+ {'SizeConstraint',{Low,High}};
+ Err ->
+ throw({error,{asn1,{illegal_size_constraint,Err}}})
+ end;
+check_constraint(S,{'SizeConstraint',Lb}) ->
+ {'SizeConstraint',resolv_value(S,Lb)};
+
+check_constraint(S,{'SingleValue', L}) when is_list(L) ->
+ F = fun(A) -> resolv_value(S,A) end,
+ {'SingleValue',lists:map(F,L)};
+
+check_constraint(S,{'SingleValue', V}) when is_integer(V) ->
+ Val = resolv_value(S,V),
+%% [{'SingleValue',Val},{'ValueRange',{Val,Val}}]; % Why adding value range?
+ {'SingleValue',Val};
+check_constraint(S,{'SingleValue', V}) ->
+ {'SingleValue',resolv_value(S,V)};
+
+check_constraint(S,{'ValueRange', {Lb, Ub}}) ->
+ {'ValueRange',{resolv_value(S,Lb),resolv_value(S,Ub)}};
+%% In case of a constraint with extension marks like (1..Ub,...)
+check_constraint(S,{VR={'ValueRange', {_Lb, _Ub}},Rest}) ->
+ {check_constraint(S,VR),Rest};
+check_constraint(_S,{'PermittedAlphabet',PA}) ->
+ {'PermittedAlphabet',permitted_alphabet_cnstr(PA)};
+
+check_constraint(S,{valueset,Type}) ->
+ {valueset,check_type(S,S#state.tname,Type)};
+
+check_constraint(_S,ST={simpletable,Type}) when is_atom(Type) ->
+ %% An already checked constraint
+ ST;
+check_constraint(S,{simpletable,Type}) ->
+ Def = case Type of
+ #type{def=D} -> D;
+ {'SingleValue',ObjRef = #'Externalvaluereference'{}} ->
+ ObjRef
+ end,
+ C = match_parameters(S,Def,S#state.parameters),
+ case C of
+ #'Externaltypereference'{} ->
+ ERef = check_externaltypereference(S,C),
+ {simpletable,ERef#'Externaltypereference'.type};
+ #type{def=#'Externaltypereference'{type=T}} ->
+ check_externaltypereference(S,C#type.def),
+ {simpletable,T};
+ {valueset,#type{def=ERef=#'Externaltypereference'{}}} -> % this is an object set
+ {_,TDef} = get_referenced_type(S,ERef),
+ case TDef#typedef.typespec of
+ #'ObjectSet'{} ->
+ check_object(S,TDef,TDef#typedef.typespec),
+ {simpletable,ERef#'Externaltypereference'.type};
+ Err ->
+ exit({error,{internal_error,Err}})
+ end;
+ #'Externalvaluereference'{} ->
+ %% This is an object set with a referenced object
+ {_,TorVDef} = get_referenced_type(S,C),
+ GetObjectSet =
+ fun(#typedef{typespec=O}) when is_record(O,'Object') ->
+ #'ObjectSet'{class=O#'Object'.classname,
+ set={'SingleValue',C}};
+ (#valuedef{type=Cl,value=O})
+ when is_record(O,'Externalvaluereference'),
+ is_record(Cl,type) ->
+ %% an object might reference another object
+ #'ObjectSet'{class=Cl#type.def,
+ set={'SingleValue',O}};
+ (Err) ->
+ exit({error,{internal_error,simpletable_constraint,Err}})
+ end,
+ ObjSet = GetObjectSet(TorVDef),
+ {simpletable,check_object(S,Type,ObjSet)};
+ #'ObjectSet'{} ->
+ io:format("ALERT: simpletable forbidden case!~n",[]),
+ {simpletable,check_object(S,Type,C)};
+ {'ValueFromObject',{_,ORef},FieldName} ->
+ %% This is an ObjectFromObject
+ {_,Object} = get_referenced_type(S,ORef),
+ ChObject = check_object(S,Object,
+ Object#typedef.typespec),
+ ObjFromObj=
+ get_fieldname_element(S,Object#typedef{
+ typespec=ChObject},
+ FieldName),
+ {simpletable,ObjFromObj};
+%% ObjFromObj#typedef{checked=true,typespec=
+%% check_object(S,ObjFromObj,
+%% ObjFromObj#typedef.typespec)}};
+ _ ->
+ check_type(S,S#state.tname,Type),%% this seems stupid.
+ OSName = Def#'Externaltypereference'.type,
+ {simpletable,OSName}
+ end;
+
+check_constraint(S,{componentrelation,{objectset,Opos,Objset},Id}) ->
+ %% Objset is an 'Externaltypereference' record, since Objset is
+ %% a DefinedObjectSet.
+ RealObjset = match_parameters(S,Objset,S#state.parameters),
+ ObjSetRef =
+ case RealObjset of
+ #'Externaltypereference'{} -> RealObjset;
+ #type{def=#'Externaltypereference'{}} -> RealObjset#type.def;
+ {valueset,OS = #type{def=#'Externaltypereference'{}}} -> OS#type.def
+ end,
+ Ext = check_externaltypereference(S,ObjSetRef),
+ {componentrelation,{objectset,Opos,Ext},Id};
+
+check_constraint(S,Type) when is_record(Type,type) ->
+ #type{def=Def} = check_type(S,S#state.tname,Type),
+ Def;
+
+check_constraint(S,C) when is_list(C) ->
+ lists:map(fun(X)->check_constraint(S,X) end,C);
+% else keep the constraint unchanged
+check_constraint(_S,Any) ->
+% io:format("Constraint = ~p~n",[Any]),
+ Any.
+
+permitted_alphabet_cnstr(T) when is_tuple(T) ->
+ permitted_alphabet_cnstr([T]);
+permitted_alphabet_cnstr(L) when is_list(L) ->
+ VRexpand = fun({'ValueRange',{A,B}}) ->
+ {'SingleValue',expand_valuerange(A,B)};
+ (Other) ->
+ Other
+ end,
+ L2 = lists:map(VRexpand,L),
+ %% first perform intersection
+ L3 = permitted_alphabet_intersection(L2),
+ [Res] = permitted_alphabet_union(L3),
+ Res.
+
+expand_valuerange([A],[A]) ->
+ [A];
+expand_valuerange([A],[B]) when A < B ->
+ [A|expand_valuerange([A+1],[B])].
+
+permitted_alphabet_intersection(C) ->
+ permitted_alphabet_merge(C,intersection, []).
+
+permitted_alphabet_union(C) ->
+ permitted_alphabet_merge(C,union, []).
+
+permitted_alphabet_merge([],_,Acc) ->
+ lists:reverse(Acc);
+permitted_alphabet_merge([{'SingleValue',L1},
+ UorI,
+ {'SingleValue',L2}|Rest],UorI,Acc)
+ when is_list(L1),is_list(L2) ->
+ UI = ordsets:UorI([ordsets:from_list(L1),ordsets:from_list(L2)]),
+ permitted_alphabet_merge([{'SingleValue',UI}|Rest],UorI,Acc);
+permitted_alphabet_merge([C1|Rest],UorI,Acc) ->
+ permitted_alphabet_merge(Rest,UorI,[C1|Acc]).
+
+
+%% constraint_merge/2
+%% Compute the intersection of the outermost level of the constraint list.
+%% See Dubuisson second paragraph and fotnote on page 285.
+%% If constraints with extension are included in combined constraints. The
+%% resulting combination will have the extension of the last constraint. Thus,
+%% there will be no extension if the last constraint is without extension.
+%% The rootset of all constraints are considered in the "outermoust
+%% intersection". See section 13.1.2 in Dubuisson.
+constraint_merge(_S,C=[H])when is_tuple(H) ->
+ C;
+constraint_merge(_S,[]) ->
+ [];
+constraint_merge(S,C) ->
+ %% skip all extension but the last extension
+ C1 = filter_extensions(C),
+ %% perform all internal level intersections, intersections first
+ %% since they have precedence over unions
+ C2 = lists:map(fun(X)when is_list(X)->constraint_intersection(S,X);
+ (X) -> X end,
+ C1),
+ %% perform all internal level unions
+ C3 = lists:map(fun(X)when is_list(X)->constraint_union(S,X);
+ (X) -> X end,
+ C2),
+
+ %% now get intersection of the outermost level
+ %% get the least common single value constraint
+ SVs = get_constraints(C3,'SingleValue'),
+ CombSV = intersection_of_sv(S,SVs),
+ %% get the least common value range constraint
+ VRs = get_constraints(C3,'ValueRange'),
+ CombVR = intersection_of_vr(S,VRs),
+ %% get the least common size constraint
+ SZs = get_constraints(C3,'SizeConstraint'),
+ CombSZ = intersection_of_size(S,SZs),
+ CminusSVs=ordsets:subtract(ordsets:from_list(C3),ordsets:from_list(SVs)),
+ % CminusSVsVRs = ordsets:subtract(ordsets:from_list(CminusSVs),
+% ordsets:from_list(VRs)),
+ RestC = ordsets:subtract(ordsets:from_list(CminusSVs),
+ ordsets:from_list(SZs)),
+ %% get the least common combined constraint. That is the union of each
+ %% deep costraint and merge of single value and value range constraints
+ NewCs = combine_constraints(S,CombSV,CombVR,CombSZ++RestC),
+ [X||X <- lists:flatten(NewCs),
+ X /= intersection,
+ X /= union].
+
+%% constraint_union(S,C) takes a list of constraints as input and
+%% merge them to a union. Unions are performed when two
+%% constraints is found with an atom union between.
+%% The list may be nested. Fix that later !!!
+constraint_union(_S,[]) ->
+ [];
+constraint_union(_S,C=[_E]) ->
+ C;
+constraint_union(S,C) when is_list(C) ->
+ case lists:member(union,C) of
+ true ->
+ constraint_union1(S,C,[]);
+ _ ->
+ C
+ end;
+% SV = get_constraints(C,'SingleValue'),
+% SV1 = constraint_union_sv(S,SV),
+% VR = get_constraints(C,'ValueRange'),
+% VR1 = constraint_union_vr(VR),
+% RestC = ordsets:filter(fun({'SingleValue',_})->false;
+% ({'ValueRange',_})->false;
+% (_) -> true end,ordsets:from_list(C)),
+% SV1++VR1++RestC;
+constraint_union(_S,C) ->
+ [C].
+
+constraint_union1(S,[A={'ValueRange',_},union,B={'ValueRange',_}|Rest],Acc) ->
+ AunionB = constraint_union_vr([A,B]),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[A={'SingleValue',_},union,B={'SingleValue',_}|Rest],Acc) ->
+ AunionB = constraint_union_sv(S,[A,B]),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[A={'SingleValue',_},union,B={'ValueRange',_}|Rest],Acc) ->
+ AunionB = union_sv_vr(S,A,B),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[A={'ValueRange',_},union,B={'SingleValue',_}|Rest],Acc) ->
+ AunionB = union_sv_vr(S,B,A),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[union|Rest],Acc) -> %skip when unsupported constraints
+ constraint_union1(S,Rest,Acc);
+constraint_union1(S,[A|Rest],Acc) ->
+ constraint_union1(S,Rest,[A|Acc]);
+constraint_union1(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+constraint_union_sv(_S,SV) ->
+ Values=lists:map(fun({_,V})->V end,SV),
+ case ordsets:from_list(Values) of
+ [] -> [];
+ [N] -> [{'SingleValue',N}];
+ L -> [{'SingleValue',L}]
+ end.
+
+%% REMOVE????
+%%constraint_union(S,VR,'ValueRange') ->
+%% constraint_union_vr(VR).
+
+%% constraint_union_vr(VR)
+%% VR = [{'ValueRange',{Lb,Ub}},...]
+%% Lb = 'MIN' | integer()
+%% Ub = 'MAX' | integer()
+%% Returns if possible only one ValueRange tuple with a range that
+%% is a union of all ranges in VR.
+constraint_union_vr(VR) ->
+ %% Sort VR by Lb in first hand and by Ub in second hand
+ Fun=fun({_,{'MIN',_B1}},{_,{A2,_B2}}) when is_integer(A2)->true;
+ ({_,{A1,_B1}},{_,{'MAX',_B2}}) when is_integer(A1) -> true;
+ ({_,{A1,_B1}},{_,{A2,_B2}}) when is_integer(A1),is_integer(A2),A1<A2 -> true;
+ ({_,{A,B1}},{_,{A,B2}}) when B1=<B2->true;
+ (_,_)->false end,
+ % sort and remove duplicates
+ SortedVR = lists:sort(Fun,VR),
+ RemoveDup = fun([],_) ->[];
+ ([H],_) -> [H];
+ ([H,H|T],F) -> F([H|T],F);
+ ([H|T],F) -> [H|F(T,F)]
+ end,
+
+ constraint_union_vr(RemoveDup(SortedVR,RemoveDup),[]).
+
+constraint_union_vr([],Acc) ->
+ lists:reverse(Acc);
+constraint_union_vr([C|Rest],[]) ->
+ constraint_union_vr(Rest,[C]);
+constraint_union_vr([{_,{Lb,Ub2}}|Rest],[{_,{Lb,_Ub1}}|Acc]) -> %Ub2 > Ub1
+ constraint_union_vr(Rest,[{'ValueRange',{Lb,Ub2}}|Acc]);
+constraint_union_vr([{_,{_,Ub}}|Rest],A=[{_,{_,Ub}}|_Acc]) ->
+ constraint_union_vr(Rest,A);
+constraint_union_vr([{_,{Lb2,Ub2}}|Rest],[{_,{Lb1,Ub1}}|Acc]) when Lb2=<Ub1,
+ Ub2>Ub1->
+ constraint_union_vr(Rest,[{'ValueRange',{Lb1,Ub2}}|Acc]);
+constraint_union_vr([{_,{_,Ub2}}|Rest],A=[{_,{_,Ub1}}|_Acc]) when Ub2=<Ub1->
+ constraint_union_vr(Rest,A);
+constraint_union_vr([VR|Rest],Acc) ->
+ constraint_union_vr(Rest,[VR|Acc]).
+
+union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',VR={Lb,Ub}})
+ when is_integer(SV) ->
+ case is_int_in_vr(SV,C2) of
+ true -> [C2];
+ _ ->
+ case VR of
+ {'MIN',Ub} when SV==Ub+1 -> [{'ValueRange',{'MIN',SV}}];
+ {Lb,'MAX'} when SV==Lb-1 -> [{'ValueRange',{SV,'MAX'}}];
+ {Lb,Ub} when SV==Ub+1 -> [{'ValueRange',{Lb,SV}}];
+ {Lb,Ub} when SV==Lb-1 -> [{'ValueRange',{SV,Ub}}];
+ _ ->
+ [C1,C2]
+ end
+ end;
+union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',{_Lb,_Ub}})
+ when is_list(SV) ->
+ case lists:filter(fun(X)->is_int_in_vr(X,C2) end,SV) of
+ [] -> [C2];
+ L ->
+ case expand_vr(L,C2) of
+ {[],C3} -> [C3];
+ {L,C2} -> [C1,C2];
+ {[Val],C3} -> [{'SingleValue',Val},C3];
+ {L2,C3} -> [{'SingleValue',L2},C3]
+ end
+ end.
+
+expand_vr(L,VR={_,{Lb,Ub}}) ->
+ case lower_Lb(L,Lb) of
+ false ->
+ case higher_Ub(L,Ub) of
+ false ->
+ {L,VR};
+ {L1,UbNew} ->
+ expand_vr(L1,{'ValueRange',{Lb,UbNew}})
+ end;
+ {L1,LbNew} ->
+ expand_vr(L1,{'ValueRange',{LbNew,Ub}})
+ end.
+
+lower_Lb(_,'MIN') ->
+ false;
+lower_Lb(L,Lb) ->
+ remove_val_from_list(Lb - 1,L).
+
+higher_Ub(_,'MAX') ->
+ false;
+higher_Ub(L,Ub) ->
+ remove_val_from_list(Ub + 1,L).
+
+remove_val_from_list(Val,List) ->
+ case lists:member(Val,List) of
+ true ->
+ {lists:delete(Val,List),Val};
+ false ->
+ false
+ end.
+
+%% get_constraints/2
+%% Arguments are a list of constraints, which has the format {key,value},
+%% and a constraint type
+%% Returns a list of constraints only of the requested type or the atom
+%% 'no' if no such constraints were found
+get_constraints(L=[{CType,_}],CType) ->
+ L;
+get_constraints(C,CType) ->
+ keysearch_allwithkey(CType,1,C).
+
+%% keysearch_allwithkey(Key,Ix,L)
+%% Types:
+%% Key = is_atom()
+%% Ix = integer()
+%% L = [TwoTuple]
+%% TwoTuple = [{atom(),term()}|...]
+%% Returns a List that contains all
+%% elements from L that has a key Key as element Ix
+keysearch_allwithkey(Key,Ix,L) ->
+ lists:filter(fun(X) when is_tuple(X) ->
+ case element(Ix,X) of
+ Key -> true;
+ _ -> false
+ end;
+ (_) -> false
+ end, L).
+
+
+%% filter_extensions(C)
+%% takes a list of constraints as input and returns a list with the
+%% constraints and all extensions but the last are removed.
+filter_extensions([L]) when is_list(L) ->
+ [filter_extensions(L)];
+filter_extensions(C=[_H]) ->
+ C;
+filter_extensions(C) when is_list(C) ->
+ filter_extensions(C,[], []).
+
+filter_extensions([],Acc,[]) ->
+ Acc;
+filter_extensions([],Acc,[EC|ExtAcc]) ->
+ CwoExt = remove_extension(ExtAcc,[]),
+ CwoExt ++ [EC|Acc];
+filter_extensions([C={A,_E}|T],Acc,ExtAcc) when is_tuple(A) ->
+ filter_extensions(T,Acc,[C|ExtAcc]);
+filter_extensions([C={'SizeConstraint',{A,_B}}|T],Acc,ExtAcc)
+ when is_list(A);is_tuple(A) ->
+ filter_extensions(T,Acc,[C|ExtAcc]);
+filter_extensions([C={'PermittedAlphabet',{{'SingleValue',_},E}}|T],Acc,ExtAcc)
+ when is_tuple(E); is_list(E) ->
+ filter_extensions(T,Acc,[C|ExtAcc]);
+filter_extensions([H|T],Acc,ExtAcc) ->
+ filter_extensions(T,[H|Acc],ExtAcc).
+
+remove_extension([],Acc) ->
+ Acc;
+remove_extension([{'SizeConstraint',{A,_B}}|R],Acc) ->
+ remove_extension(R,[{'SizeConstraint',A}|Acc]);
+remove_extension([{C,_E}|R],Acc) when is_tuple(C) ->
+ remove_extension(R,[C|Acc]);
+remove_extension([{'PermittedAlphabet',{A={'SingleValue',_},
+ E}}|R],Acc)
+ when is_tuple(E);is_list(E) ->
+ remove_extension(R,[{'PermittedAlphabet',A}|Acc]).
+
+%% constraint_intersection(S,C) takes a list of constraints as input and
+%% performs intersections. Intersecions are performed when an
+%% atom intersection is found between two constraints.
+%% The list may be nested. Fix that later !!!
+constraint_intersection(_S,[]) ->
+ [];
+constraint_intersection(_S,C=[_E]) ->
+ C;
+constraint_intersection(S,C) when is_list(C) ->
+% io:format("constraint_intersection: ~p~n",[C]),
+ case lists:member(intersection,C) of
+ true ->
+ constraint_intersection1(S,C,[]);
+ _ ->
+ C
+ end;
+constraint_intersection(_S,C) ->
+ [C].
+
+constraint_intersection1(S,[A,intersection,B|Rest],Acc) ->
+ AisecB = c_intersect(S,A,B),
+ constraint_intersection1(S,Rest,AisecB++Acc);
+constraint_intersection1(S,[A|Rest],Acc) ->
+ constraint_intersection1(S,Rest,[A|Acc]);
+constraint_intersection1(_,[],Acc) ->
+ lists:reverse(Acc).
+
+c_intersect(S,C1={'SingleValue',_},C2={'SingleValue',_}) ->
+ intersection_of_sv(S,[C1,C2]);
+c_intersect(S,C1={'ValueRange',_},C2={'ValueRange',_}) ->
+ intersection_of_vr(S,[C1,C2]);
+c_intersect(S,C1={'ValueRange',_},C2={'SingleValue',_}) ->
+ intersection_sv_vr(S,[C2],[C1]);
+c_intersect(S,C1={'SingleValue',_},C2={'ValueRange',_}) ->
+ intersection_sv_vr(S,[C1],[C2]);
+c_intersect(_S,C1,C2) ->
+ [C1,C2].
+
+%% combine_constraints(S,SV,VR,CComb)
+%% Types:
+%% S = is_record(state,S)
+%% SV = [] | [SVC]
+%% VR = [] | [VRC]
+%% CComb = [] | [Lists]
+%% SVC = {'SingleValue',integer()} | {'SingleValue',[integer(),...]}
+%% VRC = {'ValueRange',{Lb,Ub}}
+%% Lists = List of lists containing any constraint combination
+%% Lb = 'MIN' | integer()
+%% Ub = 'MAX' | integer()
+%% Returns a combination of the least common constraint among SV,VR and all
+%% elements in CComb
+combine_constraints(_S,[],VR,CComb) ->
+ VR ++ CComb;
+% combine_combined_cnstr(S,VR,CComb);
+combine_constraints(_S,SV,[],CComb) ->
+ SV ++ CComb;
+% combine_combined_cnstr(S,SV,CComb);
+combine_constraints(S,SV,VR,CComb) ->
+ C=intersection_sv_vr(S,SV,VR),
+ C ++ CComb.
+% combine_combined_cnstr(S,C,CComb).
+
+intersection_sv_vr(_S,[C1={'SingleValue',SV}],[C2={'ValueRange',{_Lb,_Ub}}])
+ when is_integer(SV) ->
+ case is_int_in_vr(SV,C2) of
+ true -> [C1];
+ _ -> %%error({type,{"asn1 illegal constraint",C1,C2},S})
+ %throw({error,{"asn1 illegal constraint",C1,C2}})
+ %io:format("warning: could not analyze constraint ~p~n",[[C1,C2]]),
+ [C1,C2]
+ end;
+intersection_sv_vr(_S,[C1={'SingleValue',SV}],[C2])
+ when is_list(SV) ->
+ case lists:filter(fun(X)->is_int_in_vr(X,C2) end,SV) of
+ [] ->
+ %%error({type,{"asn1 illegal constraint",C1,C2},S});
+ %throw({error,{"asn1 illegal constraint",C1,C2}});
+ %io:format("warning: could not analyze constraint ~p~n",[[C1,C2]]),
+ [C1,C2];
+ [V] -> [{'SingleValue',V}];
+ L -> [{'SingleValue',L}]
+ end.
+
+
+%% Size constraint [{'SizeConstraint',1},{'SizeConstraint',{{1,64},[]}}]
+
+intersection_of_size(_,[]) ->
+ [];
+intersection_of_size(_,C=[_SZ]) ->
+ C;
+intersection_of_size(S,[SZ,SZ|Rest]) ->
+ intersection_of_size(S,[SZ|Rest]);
+intersection_of_size(S,C=[C1={_,Int},{_,Range}|Rest])
+ when is_integer(Int),is_tuple(Range) ->
+ case Range of
+ {Lb,Ub} when Int >= Lb,
+ Int =< Ub ->
+ intersection_of_size(S,[C1|Rest]);
+ {{Lb,Ub},Ext} when is_list(Ext),Int >= Lb,Int =< Ub ->
+ intersection_of_size(S,[C1|Rest]);
+ _ ->
+ throw({error,{asn1,{illegal_size_constraint,C}}})
+ end;
+intersection_of_size(S,[C1={_,Range},C2={_,Int}|Rest])
+ when is_integer(Int),is_tuple(Range) ->
+ intersection_of_size(S,[C2,C1|Rest]);
+intersection_of_size(S,[{_,{Lb1,Ub1}},{_,{Lb2,Ub2}}|Rest]) ->
+ Lb=greatest_LB(ordsets:from_list([Lb1,Lb2])),
+ Ub=smallest_UB(ordsets:from_list([Ub1,Ub2])),
+ intersection_of_size(S,[{'SizeConstraint',{Lb,Ub}}|Rest]);
+intersection_of_size(_,SZ) ->
+ throw({error,{asn1,{illegal_size_constraint,SZ}}}).
+
+intersection_of_vr(_,[]) ->
+ [];
+intersection_of_vr(_,VR=[_C]) ->
+ VR;
+intersection_of_vr(S,[{_,{Lb1,Ub1}},{_,{Lb2,Ub2}}|Rest]) ->
+ Lb=greatest_LB(ordsets:from_list([Lb1,Lb2])),
+ Ub=smallest_UB(ordsets:from_list([Ub1,Ub2])),
+ intersection_of_vr(S,[{'ValueRange',{Lb,Ub}}|Rest]);
+intersection_of_vr(_S,VR) ->
+ %%error({type,{asn1,{illegal_value_range_constraint,VR}},S});
+ throw({error,{asn1,{illegal_value_range_constraint,VR}}}).
+
+intersection_of_sv(_,[]) ->
+ [];
+intersection_of_sv(_,SV=[_C]) ->
+ SV;
+intersection_of_sv(S,[SV,SV|Rest]) ->
+ intersection_of_sv(S,[SV|Rest]);
+intersection_of_sv(S,[{_,Int},{_,SV}|Rest]) when is_integer(Int),
+ is_list(SV) ->
+ SV2=intersection_of_sv1(S,Int,SV),
+ intersection_of_sv(S,[SV2|Rest]);
+intersection_of_sv(S,[{_,SV},{_,Int}|Rest]) when is_integer(Int),
+ is_list(SV) ->
+ SV2=intersection_of_sv1(S,Int,SV),
+ intersection_of_sv(S,[SV2|Rest]);
+intersection_of_sv(S,[{_,SV1},{_,SV2}|Rest]) when is_list(SV1),
+ is_list(SV2) ->
+ SV3=common_set(SV1,SV2),
+ intersection_of_sv(S,[SV3|Rest]);
+intersection_of_sv(_S,SV) ->
+ %%error({type,{asn1,{illegal_single_value_constraint,SV}},S}).
+ throw({error,{asn1,{illegal_single_value_constraint,SV}}}).
+
+intersection_of_sv1(_S,Int,SV) when is_integer(Int),is_list(SV) ->
+ case lists:member(Int,SV) of
+ true -> {'SingleValue',Int};
+ _ ->
+ %%error({type,{asn1,{illegal_single_value_constraint,Int,SV}},S})
+ throw({error,{asn1,{illegal_single_value_constraint,Int,SV}}})
+ end;
+intersection_of_sv1(_S,SV1,SV2) ->
+ %%error({type,{asn1,{illegal_single_value_constraint,SV1,SV2}},S}).
+ throw({error,{asn1,{illegal_single_value_constraint,SV1,SV2}}}).
+
+greatest_LB([H]) ->
+ H;
+greatest_LB(L) ->
+ greatest_LB1(lists:reverse(L)).
+greatest_LB1(['MIN',H2|_T])->
+ H2;
+greatest_LB1([H|_T]) ->
+ H.
+smallest_UB(L) ->
+ hd(L).
+
+common_set(SV1,SV2) ->
+ lists:filter(fun(X)->lists:member(X,SV1) end,SV2).
+
+is_int_in_vr(Int,{_,{'MIN','MAX'}}) when is_integer(Int) ->
+ true;
+is_int_in_vr(Int,{_,{'MIN',Ub}}) when is_integer(Int),Int =< Ub ->
+ true;
+is_int_in_vr(Int,{_,{Lb,'MAX'}}) when is_integer(Int),Int >= Lb ->
+ true;
+is_int_in_vr(Int,{_,{Lb,Ub}}) when is_integer(Int),Int >= Lb,Int =< Ub ->
+ true;
+is_int_in_vr(_,_) ->
+ false.
+
+
+check_imported(S,Imodule,Name) ->
+ check_imported(S,Imodule,Name,false).
+check_imported(S,Imodule,Name,IsParsed) ->
+ case asn1_db:dbget(Imodule,'MODULE') of
+ undefined when IsParsed == true ->
+ ErrStr = io_lib:format("Type ~s imported from non existing module ~s~n",[Name,Imodule]),
+ error({imported,ErrStr,S});
+ undefined ->
+ parse_and_save(S,Imodule),
+ check_imported(S,Imodule,Name,true);
+ Im when is_record(Im,module) ->
+ case is_exported(Im,Name) of
+ false ->
+ ErrStr = io_lib:format("Imported type ~s not exported from module ~s~n",[Name,Imodule]),
+ error({imported,ErrStr,S});
+ _ ->
+ ok
+ end
+ end,
+ ok.
+
+is_exported(Module,Name) when is_record(Module,module) ->
+ {exports,Exports} = Module#module.exports,
+ case Exports of
+ all ->
+ true;
+ [] ->
+ false;
+ L when is_list(L) ->
+ case lists:keysearch(Name,#'Externaltypereference'.type,Exports) of
+ false -> false;
+ _ -> true
+ end
+ end.
+
+
+check_externaltypereference(S,Etref=#'Externaltypereference'{module=Emod})->
+ Currmod = S#state.mname,
+ MergedMods = S#state.inputmodules,
+ case Emod of
+ Currmod ->
+ %% reference to current module or to imported reference
+ check_reference(S,Etref);
+ _ ->
+ %% io:format("Type ~s IMPORTED FROM ~s~n",[Etype,Emod]),
+ case lists:member(Emod,MergedMods) of
+ true ->
+ check_reference(S,Etref);
+ false ->
+ {NewMod,_} = get_referenced_type(S,Etref),
+ Etref#'Externaltypereference'{module=NewMod}
+ end
+ end.
+
+check_reference(S,#'Externaltypereference'{pos=Pos,module=Emod,type=Name}) ->
+ ModName = S#state.mname,
+ case asn1_db:dbget(ModName,Name) of
+ undefined ->
+ case imported(S,Name) of
+ {ok,Imodule} ->
+ check_imported(S,Imodule,Name),
+ #'Externaltypereference'{module=Imodule,type=Name};
+%% case check_imported(S,Imodule,Name) of
+%% ok ->
+%% #'Externaltypereference'{module=Imodule,type=Name};
+%% Err ->
+%% Err
+%% end;
+ _ ->
+ %may be a renamed type in multi file compiling!
+ {M,T}=get_renamed_reference(S,Name,Emod),
+ NewName = asn1ct:get_name_of_def(T),
+ NewPos = asn1ct:get_pos_of_def(T),
+ #'Externaltypereference'{pos=NewPos,
+ module=M,
+ type=NewName}
+ end;
+ _ ->
+ %% cannot do check_type here due to recursive definitions, like
+ %% S ::= SEQUENCE {a INTEGER, b S}. This implies that references
+ %% that appear before the definition will be an
+ %% Externaltypereference in the abstract syntax tree
+ #'Externaltypereference'{pos=Pos,module=ModName,type=Name}
+ end.
+
+
+get_referenced_type(S,Ext) when is_record(Ext,'Externaltypereference') ->
+ case match_parameters(S,Ext, S#state.parameters) of
+ Ext ->
+ #'Externaltypereference'{pos=Pos,module=Emod,type=Etype} = Ext,
+ case S#state.mname of
+ Emod -> % a local reference in this module
+ get_referenced1(S,Emod,Etype,Pos);
+ _ ->% always when multi file compiling
+ case lists:member(Emod,S#state.inputmodules) of
+ true ->
+ get_referenced1(S,Emod,Etype,Pos);
+ false ->
+ get_referenced(S,Emod,Etype,Pos)
+ end
+ end;
+ ERef = #'Externaltypereference'{} ->
+ get_referenced_type(S,ERef);
+ Other ->
+ {undefined,Other}
+ end;
+get_referenced_type(S=#state{mname=Emod},
+ ERef=#'Externalvaluereference'{pos=P,module=Emod,
+ value=Eval}) ->
+ case match_parameters(S,ERef,S#state.parameters) of
+ ERef ->
+ get_referenced1(S,Emod,Eval,P);
+ OtherERef when is_record(OtherERef,'Externalvaluereference') ->
+ get_referenced_type(S,OtherERef);
+ Value ->
+ {Emod,Value}
+ end;
+get_referenced_type(S,ERef=#'Externalvaluereference'{pos=Pos,module=Emod,
+ value=Eval}) ->
+ case match_parameters(S,ERef,S#state.parameters) of
+ ERef ->
+ case lists:member(Emod,S#state.inputmodules) of
+ true ->
+ get_referenced1(S,Emod,Eval,Pos);
+ false ->
+ get_referenced(S,Emod,Eval,Pos)
+ end;
+ OtherERef ->
+ get_referenced_type(S,OtherERef)
+ end;
+get_referenced_type(S,#identifier{val=Name,pos=Pos}) ->
+ get_referenced1(S,undefined,Name,Pos);
+get_referenced_type(_S,Type) ->
+ {undefined,Type}.
+
+%% get_referenced/3
+%% The referenced entity Ename may in case of an imported parameterized
+%% type reference imported entities in the other module, which implies that
+%% asn1_db:dbget will fail even though the referenced entity exists. Thus
+%% Emod may be the module that imports the entity Ename and not holds the
+%% data about Ename.
+get_referenced(S,Emod,Ename,Pos) ->
+ ?dbg("get_referenced: ~p~n",[Ename]),
+ parse_and_save(S,Emod),
+ ?dbg("get_referenced,parse_and_save ~n",[]),
+ case asn1_db:dbget(Emod,Ename) of
+ undefined ->
+ %% May be an imported entity in module Emod or Emod may not exist
+ case asn1_db:dbget(Emod,'MODULE') of
+ undefined ->
+ case parse_and_save(S,Emod) of
+ ok ->
+ get_referenced(S,Emod,Ename,Pos);
+ _ ->
+ throw({error,{asn1,{module_not_found,Emod}}})
+ end;
+ _ ->
+ NewS = update_state(S,Emod),
+ get_imported(NewS,Ename,Emod,Pos)
+ end;
+ T when is_record(T,typedef) ->
+ ?dbg("get_referenced T: ~p~n",[T]),
+ Spec = T#typedef.typespec, %% XXXX Spec may be something else than #type
+ case Spec of
+ #type{def=#typereference{}} ->
+ Tref = Spec#type.def,
+ Def = #'Externaltypereference'{module=Emod,
+ type=Tref#typereference.val,
+ pos=Tref#typereference.pos},
+
+
+ {Emod,T#typedef{typespec=Spec#type{def=Def}}};
+ _ ->
+ {Emod,T} % should add check that T is exported here
+ end;
+ V ->
+ ?dbg("get_referenced V: ~p~n",[V]),
+ {Emod,V}
+ end.
+
+get_referenced1(S,ModuleName,Name,Pos) ->
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ %% ModuleName may be other than S#state.mname when
+ %% multi file compiling is used.
+ get_imported(S,Name,ModuleName,Pos);
+ T ->
+ {S#state.mname,T}
+ end.
+
+get_imported(S,Name,Module,Pos) ->
+ ?dbg("get_imported, Module: ~p, Name: ~p~n",[Module,Name]),
+ case imported(S,Name) of
+ {ok,Imodule} ->
+ parse_and_save(S,Imodule),
+ case asn1_db:dbget(Imodule,'MODULE') of
+ undefined ->
+ case parse_and_save(S,Imodule) of
+ ok ->
+ %% check with cover
+ get_referenced(S,Module,Name,Pos);
+ _ ->
+ throw({error,{asn1,{module_not_found,Imodule}}})
+ end;
+ Im when is_record(Im,module) ->
+ case is_exported(Im,Name) of
+ false ->
+ throw({error,
+ {asn1,{not_exported,{Im,Name}}}});
+ _ ->
+ ?dbg("get_imported, is_exported ~p, ~p~n",[Imodule,Name]),
+ get_referenced_type(S,
+ #'Externaltypereference'
+ {module=Imodule,
+ type=Name,pos=Pos})
+ end
+ end;
+ _ ->
+ get_renamed_reference(S,Name,Module)
+ end.
+
+check_and_save(S,#'Externaltypereference'{module=M}=ERef,#typedef{checked=false}=TDef,Settings)
+ when S#state.mname /= M ->
+ %% This ERef is an imported type (or maybe a set.asn compilation)
+ NewS = S#state{mname=M,module=load_asn1_module(S,M),
+ type=TDef,tname=get_datastr_name(TDef)},
+ Type=check_type(NewS,TDef,TDef#typedef.typespec),%XXX
+ CheckedTDef = TDef#typedef{checked=true,
+ typespec=Type},
+ asn1_db:dbput(M,get_datastr_name(TDef),CheckedTDef),
+ {merged_name(S,ERef),Settings};
+check_and_save(S,#'Externaltypereference'{module=M,type=N}=Eref,
+ #ptypedef{name=Name,args=Params} = PTDef,Settings) ->
+ %% instantiate a parameterized type
+ %% The parameterized type should be saved as a type in the module
+ %% it was instantiated.
+ NewS = S#state{mname=M,module=load_asn1_module(S,M),
+ type=PTDef,tname=Name},
+ {Args,RestSettings} = lists:split(length(Params),Settings),
+ Type = check_type(NewS,PTDef,#type{def={pt,Eref,Args}}),
+ ERefName = new_reference_name(N),
+ ERefNew = #'Externaltypereference'{type=ERefName,module=S#state.mname},
+ NewTDef=#typedef{checked=true,name=ERefName,
+ typespec=Type},
+ insert_once(S,parameterized_objects,{ERefName,type,NewTDef}),
+ asn1_db:dbput(S#state.mname,ERefNew#'Externaltypereference'.type,
+ NewTDef),
+ {ERefNew,RestSettings};
+check_and_save(_S,ERef,TDef,Settings) ->
+ %% This might be a renamed type in a set of specs, so rename the ERef
+ {ERef#'Externaltypereference'{type=asn1ct:get_name_of_def(TDef)},Settings}.
+
+save_object_set_instance(S,Name,ObjSetSpec)
+ when is_record(ObjSetSpec,'ObjectSet') ->
+ NewObjSet = #typedef{checked=true,name=Name,typespec=ObjSetSpec},
+ asn1_db:dbput(S#state.mname,Name,NewObjSet),
+ case ObjSetSpec of
+ #'ObjectSet'{uniquefname={unique,undefined}} ->
+ ok;
+ _ ->
+ %% Should be generated iff
+ %% ObjSpec#'ObjectSet'.uniquefname /= {unique,undefined}
+ ObjSetKey = {Name,objectset,NewObjSet},
+ %% asn1ct_gen:insert_once(parameterized_objects,ObjSetKey)
+ insert_once(S,parameterized_objects,ObjSetKey)
+ end,
+ #'Externaltypereference'{module=S#state.mname,type=Name}.
+
+%% load_asn1_module do not check that the module is saved.
+%% If get_referenced_type is called before the module must
+%% be saved.
+load_asn1_module(#state{mname=M,module=Mod},M)->
+ Mod;
+load_asn1_module(_,M) ->
+ asn1_db:dbget(M,'MODULE').
+
+parse_and_save(S,Module) when is_record(S,state) ->
+ Erule = S#state.erule,
+ case asn1db_member(S,Erule,Module) of
+ true ->
+ ok;
+ _ ->
+ case asn1ct:parse_and_save(Module,S) of
+ ok ->
+ save_asn1db_uptodate(S,Erule,Module);
+ Err ->
+ Err
+ end
+ end.
+
+asn1db_member(S,Erule,Module) ->
+ Asn1dbUTL = get_asn1db_uptodate(S),
+ lists:member({Erule,Module},Asn1dbUTL).
+
+save_asn1db_uptodate(S,Erule,Module) ->
+ Asn1dbUTL = get_asn1db_uptodate(S),
+ Asn1dbUTL2 = lists:keydelete(Module,2,Asn1dbUTL),
+ put_asn1db_uptodate([{Erule,Module}|Asn1dbUTL2]).
+
+get_asn1db_uptodate(S) ->
+ case get(asn1db_uptodate) of
+ undefined -> [{S#state.erule,S#state.mname}]; %initialize
+ L -> L
+ end.
+
+put_asn1db_uptodate(L) ->
+ put(asn1db_uptodate,L).
+
+update_state(S,undefined) ->
+ S;
+update_state(S=#state{mname=ModuleName},ModuleName) ->
+ S;
+update_state(S,ModuleName) ->
+ case lists:member(ModuleName,S#state.inputmodules) of
+ true ->
+ S;
+ _ ->
+ parse_and_save(S,ModuleName),
+ case asn1_db:dbget(ModuleName,'MODULE') of
+ RefedMod when is_record(RefedMod,module) ->
+ S#state{mname=ModuleName,module=RefedMod};
+ _ -> throw({error,{asn1,{module_does_not_exist,ModuleName}}})
+ end
+ end.
+
+
+get_renamed_reference(S,Name,Module) ->
+ case renamed_reference(S,Name,Module) of
+ undefined ->
+ throw({error,{asn1,{undefined_type,Name}}});
+ NewTypeName when NewTypeName =/= Name ->
+ get_referenced1(S,Module,NewTypeName,undefined)
+ end.
+renamed_reference(S,#'Externaltypereference'{type=Name,module=Module}) ->
+ case renamed_reference(S,Name,Module) of
+ undefined ->
+ Name;
+ Other ->
+ Other
+ end.
+renamed_reference(S,Name,Module) ->
+ %% first check if there is a renamed type in this module
+ %% second check if any type was imported with this name
+ case ets:info(renamed_defs) of
+ undefined -> undefined;
+ _ ->
+ case ets:match(renamed_defs,{'$1',Name,Module}) of
+ [] ->
+ case ets:info(original_imports) of
+ undefined ->
+ undefined;
+ _ ->
+ case ets:match(original_imports,{Module,'$1'}) of
+ [] ->
+ undefined;
+ [[ImportsList]] ->
+ case get_importmoduleoftype(ImportsList,Name) of
+ undefined ->
+ undefined;
+ NextMod ->
+ renamed_reference(S,Name,NextMod)
+ end
+ end
+ end;
+ [[NewTypeName]] ->
+ NewTypeName
+ end
+ end.
+
+get_importmoduleoftype([I|Is],Name) ->
+ Index = #'Externaltypereference'.type,
+ case lists:keysearch(Name,Index,I#'SymbolsFromModule'.symbols) of
+ {value,_Ref} ->
+ (I#'SymbolsFromModule'.module)#'Externaltypereference'.type;
+ _ ->
+ get_importmoduleoftype(Is,Name)
+ end;
+get_importmoduleoftype([],_) ->
+ undefined.
+
+
+match_parameters(_S,Name,[]) ->
+ Name;
+
+match_parameters(_S,#'Externaltypereference'{type=Name},[{#'Externaltypereference'{type=Name},NewName}|_T]) ->
+ NewName;
+match_parameters(_S,#'Externaltypereference'{type=Name},[{{_,#'Externaltypereference'{type=Name}},NewName}|_T]) ->
+ NewName;
+match_parameters(_S,#'Externalvaluereference'{value=Name},[{#'Externalvaluereference'{value=Name},NewName}|_T]) ->
+ NewName;
+match_parameters(_S,#'Externalvaluereference'{value=Name},[{{_,#'Externalvaluereference'{value=Name}},NewName}|_T]) ->
+ NewName;
+match_parameters(_S,#type{def=#'Externaltypereference'{module=M,type=Name}},
+ [{#'Externaltypereference'{module=M,type=Name},Type}]) ->
+ Type;
+match_parameters(_S,{valueset,#type{def=#'Externaltypereference'{type=Name}}},
+ [{{_,#'Externaltypereference'{type=Name}},{valueset,#type{def=NewName}}}|_T]) ->
+ NewName;
+match_parameters(_S,{valueset,#type{def=#'Externaltypereference'{type=Name}}},
+ [{{_,#'Externaltypereference'{type=Name}},
+ NewName=#type{def=#'Externaltypereference'{}}}|_T]) ->
+ NewName#type.def;
+match_parameters(_S,{valueset,#type{def=#'Externaltypereference'{type=Name}}},
+ [{{_,#'Externaltypereference'{type=Name}},NewName}|_T]) ->
+ NewName;
+%% When a parameter is a parameterized element it has to be
+%% instantiated now!
+match_parameters(S,{valueset,T=#type{def={pt,_,_Args}}},_Parameters) ->
+ case catch check_type(S,#typedef{name=S#state.tname,typespec=T},T) of
+ pobjectsetdef ->
+
+ {_,ObjRef,_Params} = T#type.def,
+ {_,ObjDef}=get_referenced_type(S,ObjRef),
+ %%ObjDef is a pvaluesetdef where the type field holds the class
+ ClassRef =
+ case ObjDef of
+ #pvaluesetdef{type=TDef} ->
+ TDef#type.def;
+ #pobjectsetdef{class=ClRef} -> ClRef
+ end,
+ %% The reference may not have the home module of the class
+ {HomeMod,_} = get_referenced_type(S,ClassRef),
+ RightClassRef =
+ ClassRef#'Externaltypereference'{module=HomeMod},
+
+ ObjectSet = #'ObjectSet'{class=RightClassRef,set=T},
+ ObjSpec = check_object(S,#typedef{typespec=ObjectSet},ObjectSet),
+ Name = list_to_atom(asn1ct_gen:list2name([get_datastr_name(ObjDef)|S#state.recordtopname])),
+ save_object_set_instance(S,Name,ObjSpec);
+ pvaluesetdef -> error({pvaluesetdef,"parameterized valueset",S});
+ {error,_Reason} -> error({type,"error in parameter",S});
+ Ts when is_record(Ts,type) -> Ts#type.def
+ end;
+%% same as previous, only depends on order of parsing
+match_parameters(S,{valueset,{pos,{objectset,_,POSref},Args}},Parameters) ->
+ match_parameters(S,{valueset,#type{def={pt,POSref,Args}}},Parameters);
+match_parameters(S,Name, [_H|T]) ->
+ %%io:format("match_parameters(~p,~p)~n",[Name,[H|T]]),
+ match_parameters(S,Name,T).
+
+imported(S,Name) ->
+ {imports,Ilist} = (S#state.module)#module.imports,
+ imported1(Name,Ilist).
+
+imported1(Name,
+ [#'SymbolsFromModule'{symbols=Symlist,
+ module=#'Externaltypereference'{type=ModuleName}}|T]) ->
+ case lists:keysearch(Name,#'Externaltypereference'.type,Symlist) of
+ {value,_V} ->
+ {ok,ModuleName};
+ _ ->
+ imported1(Name,T)
+ end;
+imported1(_Name,[]) ->
+ false.
+
+
+check_integer(_S,[],_C) ->
+ [];
+check_integer(S,NamedNumberList,_C) ->
+ case [X||X<-NamedNumberList,is_tuple(X),size(X)=:=2] of
+ NamedNumberList ->
+ %% An already checked integer with NamedNumberList
+ NamedNumberList;
+ _ ->
+ case check_unique(NamedNumberList,2) of
+ [] ->
+ check_int(S,NamedNumberList,[]);
+ L when is_list(L) ->
+ error({type,{duplicates,L},S}),
+ unchanged
+ end
+ end.
+
+
+check_int(S,[{'NamedNumber',Id,Num}|T],Acc) when is_integer(Num) ->
+ check_int(S,T,[{Id,Num}|Acc]);
+check_int(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
+ Val = dbget_ex(S,S#state.mname,Name),
+ check_int(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
+check_int(_S,[],Acc) ->
+ lists:keysort(2,Acc).
+
+check_real(_S,_Constr) ->
+ ok.
+
+check_bitstring(_S,[],_Constr) ->
+ [];
+check_bitstring(S,NamedNumberList,_Constr) ->
+ case check_unique(NamedNumberList,2) of
+ [] ->
+ check_bitstr(S,NamedNumberList,[]);
+ L when is_list(L) ->
+ error({type,{duplicates,L},S}),
+ unchanged
+ end.
+
+check_bitstr(S,[{'NamedNumber',Id,Num}|T],Acc)when is_integer(Num) ->
+ check_bitstr(S,T,[{Id,Num}|Acc]);
+check_bitstr(S,[{'NamedNumber',Id,Name}|T],Acc) when is_atom(Name) ->
+%%check_bitstr(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
+%% io:format("asn1ct_check:check_bitstr/3 hej hop ~w~n",[Name]),
+ Val = dbget_ex(S,S#state.mname,Name),
+%% io:format("asn1ct_check:check_bitstr/3: ~w~n",[Val]),
+ check_bitstr(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
+check_bitstr(S,[],Acc) ->
+ case check_unique(Acc,2) of
+ [] ->
+ lists:keysort(2,Acc);
+ L when is_list(L) ->
+ error({type,{duplicate_values,L},S}),
+ unchanged
+ end;
+%% When a BIT STRING already is checked, for instance a COMPONENTS OF S
+%% where S is a sequence that has a component that is a checked BS, the
+%% NamedNumber list is a list of {atom(),integer()} elements.
+check_bitstr(S,[El={Id,Num}|Rest],Acc) when is_atom(Id),is_integer(Num) ->
+ check_bitstr(S,Rest,[El|Acc]).
+
+
+%% Check INSTANCE OF
+%% check that DefinedObjectClass is of TYPE-IDENTIFIER class
+%% If Constraint is empty make it the general INSTANCE OF type
+%% If Constraint is not empty make an inlined type
+%% convert INSTANCE OF to the associated type
+check_instance_of(S,DefinedObjectClass,Constraint) ->
+ check_type_identifier(S,DefinedObjectClass),
+ iof_associated_type(S,Constraint).
+
+
+check_type_identifier(_S,'TYPE-IDENTIFIER') ->
+ ok;
+check_type_identifier(S,Eref=#'Externaltypereference'{}) ->
+ case get_referenced_type(S,Eref) of
+ {_,#classdef{name='TYPE-IDENTIFIER'}} -> ok;
+ {_,#classdef{typespec=NextEref}}
+ when is_record(NextEref,'Externaltypereference') ->
+ check_type_identifier(S,NextEref);
+ {_,TD=#typedef{typespec=#type{def=#'Externaltypereference'{}}}} ->
+ check_type_identifier(S,(TD#typedef.typespec)#type.def);
+ Err ->
+ error({type,{"object set in type INSTANCE OF "
+ "not of class TYPE-IDENTIFIER",Eref,Err},S})
+ end.
+
+iof_associated_type(S,[]) ->
+ %% in this case encode/decode functions for INSTANCE OF must be
+ %% generated
+ case get(instance_of) of
+ undefined ->
+ AssociateSeq = iof_associated_type1(S,[]),
+ Tag =
+ case S#state.erule of
+ ber_bin_v2 ->
+ [?TAG_CONSTRUCTED(?N_INSTANCE_OF)];
+ _ -> []
+ end,
+ TypeDef=#typedef{checked=true,
+ name='INSTANCE OF',
+ typespec=#type{tag=Tag,
+ def=AssociateSeq}},
+ asn1_db:dbput(S#state.mname,'INSTANCE OF',TypeDef),
+ instance_of_decl(S#state.mname);
+%% put(instance_of,{generate,S#state.mname});
+ _ ->
+ instance_of_decl(S#state.mname),
+ ok
+ end,
+ #'Externaltypereference'{module=S#state.mname,type='INSTANCE OF'};
+iof_associated_type(S,C) ->
+ iof_associated_type1(S,C).
+
+iof_associated_type1(S,C) ->
+ {TableCInf,Comp1Cnstr,Comp2Cnstr,Comp2tablecinf}=
+ instance_of_constraints(S,C),
+
+ ModuleName = S#state.mname,
+ Typefield_type=
+ case C of
+ [] -> 'ASN1_OPEN_TYPE';
+ _ -> {typefield,'Type'}
+ end,
+ {ObjIdTag,C1TypeTag}=
+ case S#state.erule of
+ ber_bin_v2 ->
+ {[{'UNIVERSAL',8}],
+ [#tag{class='UNIVERSAL',
+ number=6,
+ type='IMPLICIT',
+ form=0}]};
+ _ -> {[{'UNIVERSAL','INTEGER'}],[]}
+ end,
+ TypeIdentifierRef=#'Externaltypereference'{module=ModuleName,
+ type='TYPE-IDENTIFIER'},
+ ObjectIdentifier =
+ #'ObjectClassFieldType'{classname=TypeIdentifierRef,
+ class=[],
+%% fieldname=[{valuefieldreference,id}],
+ fieldname={id,[]},
+ type={fixedtypevaluefield,id,
+ #type{def='OBJECT IDENTIFIER'}}},
+ Typefield =
+ #'ObjectClassFieldType'{classname=TypeIdentifierRef,
+ class=[],
+%% fieldname=[{typefieldreference,'Type'}],
+ fieldname={'Type',[]},
+ type=Typefield_type},
+ IOFComponents =
+ [#'ComponentType'{name='type-id',
+ typespec=#type{tag=C1TypeTag,
+ def=ObjectIdentifier,
+ constraint=Comp1Cnstr},
+ prop=mandatory,
+ tags=ObjIdTag},
+ #'ComponentType'{name=value,
+ typespec=#type{tag=[#tag{class='CONTEXT',
+ number=0,
+ type='EXPLICIT',
+ form=32}],
+ def=Typefield,
+ constraint=Comp2Cnstr,
+ tablecinf=Comp2tablecinf},
+ prop=mandatory,
+ tags=[{'CONTEXT',0}]}],
+ #'SEQUENCE'{tablecinf=TableCInf,
+ components=IOFComponents}.
+
+
+%% returns the leading attribute, the constraint of the components and
+%% the tablecinf value for the second component.
+instance_of_constraints(_,[]) ->
+ {false,[],[],[]};
+instance_of_constraints(S,#constraint{c={simpletable,Type}}) ->
+ #type{def=#'Externaltypereference'{type=Name}} = Type,
+ ModuleName = S#state.mname,
+ ObjectSetRef=#'Externaltypereference'{module=ModuleName,
+ type=Name},
+ CRel=[{componentrelation,{objectset,
+ undefined, %% pos
+ ObjectSetRef},
+ [{innermost,
+ [#'Externalvaluereference'{module=ModuleName,
+ value=type}]}]}],
+ TableCInf=#simpletableattributes{objectsetname=Name,
+ c_name='type-id',
+ c_index=1,
+ usedclassfield=id,
+ uniqueclassfield=id,
+ valueindex=[]},
+ {TableCInf,[{simpletable,Name}],CRel,[{objfun,ObjectSetRef}]}.
+
+%% Check ENUMERATED
+%% ****************************************
+%% Check that all values are unique
+%% assign values to un-numbered identifiers
+%% check that the constraints are allowed and correct
+%% put the updated info back into database
+check_enumerated(_S,[{Name,Number}|Rest],_Constr) when is_atom(Name), is_integer(Number)->
+ %% already checked , just return the same list
+ [{Name,Number}|Rest];
+check_enumerated(S,NamedNumberList,_Constr) ->
+ check_enum(S,NamedNumberList,[],[],[]).
+
+%% identifiers are put in Acc2
+%% returns either [{Name,Number}] or {[{Name,Number}],[{ExtName,ExtNumber}]}
+%% the latter is returned if the ENUMERATION contains EXTENSIONMARK
+check_enum(S,[{'NamedNumber',Id,Num}|T],Acc1,Acc2,Root) when is_integer(Num) ->
+ check_enum(S,T,[{Id,Num}|Acc1],Acc2,Root);
+check_enum(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc1,Acc2,Root) ->
+ Val = dbget_ex(S,S#state.mname,Name),
+ check_enum(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc1,Acc2,Root);
+check_enum(S,['EXTENSIONMARK'|T],Acc1,Acc2,_Root) ->
+ NewAcc2 = lists:keysort(2,Acc1),
+ NewList = enum_number(lists:reverse(Acc2),NewAcc2,0,[],[]),
+ { NewList, check_enum(S,T,[],[],enum_counts(NewList))};
+check_enum(S,[Id|T],Acc1,Acc2,Root) when is_atom(Id) ->
+ check_enum(S,T,Acc1,[Id|Acc2],Root);
+check_enum(_S,[],Acc1,Acc2,Root) ->
+ NewAcc2 = lists:keysort(2,Acc1),
+ enum_number(lists:reverse(Acc2),NewAcc2,0,[],Root).
+
+
+% assign numbers to identifiers , numbers from 0 ... but must not
+% be the same as already assigned to NamedNumbers
+enum_number(Identifiers,NamedNumbers,Cnt,Acc,[]) ->
+ enum_number(Identifiers,NamedNumbers,Cnt,Acc);
+enum_number(Identifiers,NamedNumbers,_Cnt,Acc,CountL) ->
+ enum_extnumber(Identifiers,NamedNumbers,Acc,CountL).
+
+enum_number([H|T],[{Id,Num}|T2],Cnt,Acc) when Num > Cnt ->
+ enum_number(T,[{Id,Num}|T2],Cnt+1,[{H,Cnt}|Acc]);
+enum_number([H|T],[{Id,Num}|T2],Cnt,Acc) when Num < Cnt -> % negative Num
+ enum_number(T,T2,Cnt+1,[{H,Cnt},{Id,Num}|Acc]);
+enum_number([],L2,_Cnt,Acc) ->
+ lists:append([lists:reverse(Acc),L2]);
+enum_number(L,[{Id,Num}|T2],Cnt,Acc) -> % Num == Cnt
+ enum_number(L,T2,Cnt+1,[{Id,Num}|Acc]);
+enum_number([H|T],[],Cnt,Acc) ->
+ enum_number(T,[],Cnt+1,[{H,Cnt}|Acc]).
+
+enum_extnumber(Identifiers,NamedNumbers,Acc,[C]) ->
+ check_add_enum_numbers(NamedNumbers,[C]),
+ enum_number(Identifiers,NamedNumbers,C,Acc);
+enum_extnumber([H|T],[{Id,Num}|T2],Acc,[C|Counts]) when Num > C ->
+ enum_extnumber(T,[{Id,Num}|T2],[{H,C}|Acc],Counts);
+enum_extnumber([],L2,Acc,Cnt) ->
+ check_add_enum_numbers(L2, Cnt),
+ lists:concat([lists:reverse(Acc),L2]);
+enum_extnumber(_Identifiers,[{Id,Num}|_T2],_Acc,[C|_]) when Num < C ->
+%% enum_extnumber(Identifiers,T2,[{Id,Num}|Acc],Counts);
+ exit({error,{asn1,"AdditionalEnumeration element with same number as root element",{Id,Num}}});
+enum_extnumber(Identifiers,[{Id,Num}|T2],Acc,[_C|Counts]) -> % Num =:= C
+ enum_extnumber(Identifiers,T2,[{Id,Num}|Acc],Counts);
+enum_extnumber([H|T],[],Acc,[C|Counts]) ->
+ enum_extnumber(T,[],[{H,C}|Acc],Counts).
+
+enum_counts([]) ->
+ [0];
+enum_counts(L) ->
+ Used=[I||{_,I}<-L],
+ AddEnumLb = lists:max(Used) + 1,
+ lists:foldl(fun(El,AccIn)->lists:delete(El,AccIn) end,
+ lists:seq(0,AddEnumLb),
+ Used).
+check_add_enum_numbers(L, Cnt) ->
+ Max = lists:max(Cnt),
+ Fun = fun({_,N}=El) when N < Max ->
+ case lists:member(N,Cnt) of
+ false ->
+ exit({error,{asn1,"AdditionalEnumeration element with same number as root element",El}});
+ _ ->
+ ok
+ end;
+ (_) ->
+ ok
+ end,
+ lists:foreach(Fun,L).
+
+
+check_boolean(_S,_Constr) ->
+ ok.
+
+check_octetstring(_S,_Constr) ->
+ ok.
+
+% check all aspects of a SEQUENCE
+% - that all component names are unique
+% - that all TAGS are ok (when TAG default is applied)
+% - that each component is of a valid type
+% - that the extension marks are valid
+
+check_sequence(S,Type,Comps) ->
+ Components = expand_components(S,Comps),
+ case check_unique([C||C <- Components ,is_record(C,'ComponentType')]
+ ,#'ComponentType'.name) of
+ [] ->
+ %% sort_canonical(Components),
+ Components2 = maybe_automatic_tags(S,Components),
+ %% check the table constraints from here. The outermost type
+ %% is Type, the innermost is Comps (the list of components)
+ NewComps =
+ case check_each_component(S,Type,Components2) of
+ NewComponents when is_list(NewComponents) ->
+ check_unique_sequence_tags(S,NewComponents),
+ NewComponents;
+ Ret = {NewComponents,NewEcomps} ->
+ TagComps = NewComponents ++
+ [Comp#'ComponentType'{prop='OPTIONAL'}|| Comp <- NewEcomps],
+ %% extension components are like optionals when it comes to tagging
+ check_unique_sequence_tags(S,TagComps),
+ Ret;
+ Ret = {Root1,NewE,Root2} ->
+ TagComps = Root1 ++ [Comp#'ComponentType'{prop='OPTIONAL'}|| Comp <- NewE]++Root2,
+ %% This is not correct handling if Extension
+ %% contains ExtensionAdditionGroups
+ check_unique_sequence_tags(S,TagComps),
+ Ret
+ end,
+ %% CRelInf is the "leading attribute" information
+ %% necessary for code generating of the look up in the
+ %% object set table,
+ %% i.e. getenc_ObjectSet/getdec_ObjectSet.
+ %% {objfun,ERef} tuple added in NewComps2 in tablecinf
+ %% field in type record of component relation constrained
+ %% type
+ {CRelInf,NewComps2} = componentrelation_leadingattr(S,NewComps),
+
+ %% CompListWithTblInf has got a lot unecessary info about
+ %% the involved class removed, as the class of the object
+ %% set.
+ CompListWithTblInf = get_tableconstraint_info(S,Type,NewComps2),
+
+ {CRelInf,CompListWithTblInf};
+ Dupl ->
+ throw({error,{asn1,{duplicate_components,Dupl}}})
+ end.
+
+expand_components(S, [{'COMPONENTS OF',Type}|T]) ->
+ CompList = expand_components2(S,get_referenced_type(S,Type#type.def)),
+ expand_components(S,CompList) ++ expand_components(S,T);
+expand_components(S,[H|T]) ->
+ [H|expand_components(S,T)];
+expand_components(_,[]) ->
+ [].
+expand_components2(_S,{_,#typedef{typespec=#type{def=Seq}}})
+ when is_record(Seq,'SEQUENCE') ->
+ case Seq#'SEQUENCE'.components of
+ {R1,_Ext,R2} -> R1 ++ R2;
+ {Root,_Ext} -> Root;
+ Root -> Root
+ end;
+expand_components2(_S,{_,#typedef{typespec=#type{def=Set}}})
+ when is_record(Set,'SET') ->
+ case Set#'SET'.components of
+ {R1,_Ext,R2} -> R1 ++ R2;
+ {Root,_Ext} -> Root;
+ Root -> Root
+ end;
+expand_components2(_S,{_,#typedef{typespec=RefType=#type{def=#'Externaltypereference'{}}}}) ->
+ [{'COMPONENTS OF',RefType}];
+expand_components2(S,{_,PT={pt,_,_}}) ->
+ PTType = check_type(S,PT,#type{def=PT}),
+ expand_components2(S,{dummy,#typedef{typespec=PTType}});
+expand_components2(S,{_,OCFT = #'ObjectClassFieldType'{}}) ->
+ UncheckedType = #type{def=OCFT},
+ Type = check_type(S,#typedef{typespec=UncheckedType},UncheckedType),
+ expand_components2(S,{undefined,oCFT_def(S,Type)});
+expand_components2(S,{_,ERef}) when is_record(ERef,'Externaltypereference') ->
+ expand_components2(S,get_referenced_type(S,ERef));
+expand_components2(_S,Err) ->
+ throw({error,{asn1,{illegal_COMPONENTS_OF,Err}}}).
+
+
+check_unique_sequence_tags(S,[#'ComponentType'{prop=mandatory}|Rest]) ->
+ check_unique_sequence_tags(S,Rest);
+check_unique_sequence_tags(S,[C|Rest]) when is_record(C,'ComponentType') ->
+ check_unique_sequence_tags1(S,Rest,[C]);% optional or default
+check_unique_sequence_tags(S,[_ExtensionMarker|Rest]) ->
+ check_unique_sequence_tags(S,Rest);
+check_unique_sequence_tags(_S,[]) ->
+ true.
+
+check_unique_sequence_tags1(S,[C|Rest],Acc) when is_record(C,'ComponentType') ->
+ case C#'ComponentType'.prop of
+ mandatory ->
+ check_unique_tags(S,lists:reverse([C|Acc])),
+ check_unique_sequence_tags(S,Rest);
+ _ ->
+ check_unique_sequence_tags1(S,Rest,[C|Acc]) % default or optional
+ end;
+check_unique_sequence_tags1(S,[H|Rest],Acc) ->
+ check_unique_sequence_tags1(S,Rest,[H|Acc]);
+check_unique_sequence_tags1(S,[],Acc) ->
+ check_unique_tags(S,lists:reverse(Acc)).
+
+check_sequenceof(S,Type,Component) when is_record(Component,type) ->
+ check_type(S,Type,Component).
+
+check_set(S,Type,Components) ->
+ {TableCInf,NewComponents} = check_sequence(S,Type,Components),
+ check_distinct_tags(NewComponents,[]),
+ case {lists:member(der,S#state.options),S#state.erule} of
+ {true,_} ->
+ {Sorted,SortedComponents} = sort_components(der,S,NewComponents),
+ {Sorted,TableCInf,SortedComponents};
+ {_,PER} when PER =:= per; PER =:= per_bin; PER =:= uper_bin ->
+ {Sorted,SortedComponents} = sort_components(per,S,NewComponents),
+ {Sorted,TableCInf,SortedComponents};
+ _ ->
+ {false,TableCInf,NewComponents}
+ end.
+
+
+%% check that all tags are distinct according to X.680 26.3
+check_distinct_tags({C1,C2,C3},Acc) when is_list(C1),is_list(C2),is_list(C3) ->
+ check_distinct_tags(C1++C2++C3,Acc);
+check_distinct_tags({C1,C2},Acc) when is_list(C1),is_list(C2) ->
+ check_distinct_tags(C1++C2,Acc);
+check_distinct_tags([#'ComponentType'{tags=[T]}|Cs],Acc) ->
+ check_distinct(T,Acc),
+ check_distinct_tags(Cs,[T|Acc]);
+check_distinct_tags([C=#'ComponentType'{tags=[T|Ts]}|Cs],Acc) ->
+ check_distinct(T,Acc),
+ check_distinct_tags([C#'ComponentType'{tags=Ts}|Cs],[T|Acc]);
+check_distinct_tags([#'ComponentType'{tags=[]}|_Cs],_Acc) ->
+ throw({error,"Not distinct tags in SET"});
+check_distinct_tags([],_) ->
+ ok.
+check_distinct(T,Acc) ->
+ case lists:member(T,Acc) of
+ true ->
+ throw({error,"Not distinct tags in SET"});
+ _ -> ok
+ end.
+
+%% sorting in canonical order according to X.680 8.6, X.691 9.2
+%% DER: all components shall be sorted in canonical order.
+%% PER: only root components shall be sorted in canonical order. The
+%% extension components shall remain in textual order.
+%%
+sort_components(der,S=#state{tname=TypeName},Components) ->
+ {R1,Ext,R2} = extension(textual_order(Components)),
+ CompsList = case Ext of
+ noext -> R1;
+ _ -> R1 ++ Ext ++ R2
+ end,
+ case {untagged_choice(S,CompsList),Ext} of
+ {false,noext} ->
+ {true,sort_components1(TypeName,CompsList,[],[],[],[])};
+ {false,_} ->
+ {true,{sort_components1(TypeName,CompsList,[],[],[],[]), []}};
+ {true,noext} ->
+ %% sort in run-time
+ {dynamic,R1};
+ _ ->
+ {dynamic,{R1, Ext, R2}}
+ end;
+sort_components(per,S=#state{tname=TypeName},Components) ->
+ {R1,Ext,R2} = extension(textual_order(Components)),
+ Root = tag_untagged_choice(S,R1++R2),
+ case Ext of
+ noext ->
+ {true,sort_components1(TypeName,Root,[],[],[],[])};
+ _ ->
+ {true,{sort_components1(TypeName,Root,[],[],[],[]),
+ Ext}}
+ end.
+
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'UNIVERSAL',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,[C|UnivAcc],ApplAcc,ContAcc,PrivAcc);
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'APPLICATION',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,UnivAcc,[C|ApplAcc],ContAcc,PrivAcc);
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'CONTEXT',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,UnivAcc,ApplAcc,[C|ContAcc],PrivAcc);
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'PRIVATE',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,UnivAcc,ApplAcc,ContAcc,[C|PrivAcc]);
+sort_components1(TypeName,[],UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ I = #'ComponentType'.tags,
+ ascending_order_check(TypeName,sort_universal_type(UnivAcc)) ++
+ ascending_order_check(TypeName,lists:keysort(I,ApplAcc)) ++
+ ascending_order_check(TypeName,lists:keysort(I,ContAcc)) ++
+ ascending_order_check(TypeName,lists:keysort(I,PrivAcc)).
+
+ascending_order_check(TypeName,Components) ->
+ ascending_order_check1(TypeName,Components),
+ Components.
+
+ascending_order_check1(TypeName,
+ [C1 = #'ComponentType'{tags=[{_,T}|_]},
+ C2 = #'ComponentType'{tags=[{_,T}|_]}|Rest]) ->
+ io:format("WARNING: Indistinct tag ~p in SET ~p, components ~p and ~p~n",
+ [T,TypeName,C1#'ComponentType'.name,C2#'ComponentType'.name]),
+ ascending_order_check1(TypeName,[C2|Rest]);
+ascending_order_check1(TypeName,
+ [C1 = #'ComponentType'{tags=[{'UNIVERSAL',T1}|_]},
+ C2 = #'ComponentType'{tags=[{'UNIVERSAL',T2}|_]}|Rest]) ->
+ case (decode_type(T1) == decode_type(T2)) of
+ true ->
+ io:format("WARNING: Indistinct tags ~p and ~p in"
+ " SET ~p, components ~p and ~p~n",
+ [T1,T2,TypeName,C1#'ComponentType'.name,
+ C2#'ComponentType'.name]),
+ ascending_order_check1(TypeName,[C2|Rest]);
+ _ ->
+ ascending_order_check1(TypeName,[C2|Rest])
+ end;
+ascending_order_check1(N,[_|Rest]) ->
+ ascending_order_check1(N,Rest);
+ascending_order_check1(_,[]) ->
+ ok.
+
+sort_universal_type(Components) ->
+ List = lists:map(fun(C) ->
+ #'ComponentType'{tags=[{_,T}|_]} = C,
+ {decode_type(T),C}
+ end,
+ Components),
+ SortedList = lists:keysort(1,List),
+ lists:map(fun(X)->element(2,X) end,SortedList).
+
+decode_type(I) when is_integer(I) ->
+ I;
+decode_type(T) ->
+ asn1ct_gen_ber:decode_type(T).
+
+untagged_choice(_S,[#'ComponentType'{typespec=#type{tag=[],def={'CHOICE',_}}}|_Rest]) ->
+ true;
+untagged_choice(S,[#'ComponentType'{typespec=#type{tag=[],def=ExRef}}|Rest])
+ when is_record(ExRef,'Externaltypereference')->
+ case get_referenced_type(S,ExRef) of
+ {_,#typedef{typespec=#type{tag=[],
+ def={'CHOICE',_}}}} -> true;
+ _ -> untagged_choice(S,Rest)
+ end;
+untagged_choice(S,[_|Rest]) ->
+ untagged_choice(S,Rest);
+untagged_choice(_,[]) ->
+ false.
+
+
+tag_untagged_choice(S,Cs) ->
+ tag_untagged_choice(S,Cs,[]).
+tag_untagged_choice(S,[C = #'ComponentType'{typespec=#type{tag=[],def={'CHOICE',_}}}|Rest],Acc) ->
+ TagList = C#'ComponentType'.tags,
+ TaggedC = C#'ComponentType'{tags=get_least_tag(TagList)},
+ tag_untagged_choice(S,Rest,[TaggedC|Acc]);
+tag_untagged_choice(S,[C = #'ComponentType'{typespec=#type{tag=[],def=ExRef}}|Rest],Acc) when is_record(ExRef,'Externaltypereference') ->
+ case get_referenced_type(S,ExRef) of
+ {_,#typedef{typespec=#type{tag=[],
+ def={'CHOICE',_}}}} ->
+ TagList = C#'ComponentType'.tags,
+ TaggedC = C#'ComponentType'{tags = get_least_tag(TagList)},
+ tag_untagged_choice(S,Rest,[TaggedC|Acc]);
+ _ ->
+ tag_untagged_choice(S,Rest,[C|Acc])
+ end;
+tag_untagged_choice(S,[C|Rest],Acc) ->
+ tag_untagged_choice(S,Rest,[C|Acc]);
+tag_untagged_choice(_S,[],Acc) ->
+ Acc.
+get_least_tag([]) ->
+ [];
+get_least_tag(TagList) ->
+ %% The smallest tag 'PRIVATE' < 'CONTEXT' < 'APPLICATION' < 'UNIVERSAL'
+ Pred = fun({'PRIVATE',_},{'CONTEXT',_}) -> true;
+ ({'CONTEXT',_},{'APPLICATION',_}) -> true;
+ ({'APPLICATION',_},{'UNIVERSAL',_}) -> true;
+ ({A,T1},{A,T2}) when T1 =< T2 -> true; (_,_) -> false
+ end,
+ [T|_] = lists:sort(Pred,TagList),
+ [T].
+
+%% adds the textual order to the components to keep right order of
+%% components in the asn1-value.
+textual_order(Cs) ->
+ Fun = fun(C,Index) ->
+ {C#'ComponentType'{textual_order=Index},Index+1}
+ end,
+ {NewCs,_} = textual_order(Cs,Fun,1),
+ NewCs.
+textual_order(Cs,Fun,IxIn) when is_list(Cs) ->
+ lists:mapfoldl(Fun,IxIn,Cs);
+textual_order({Root,Ext},Fun,IxIn) ->
+ {NewRoot,IxR} = textual_order(Root,Fun,IxIn),
+ {NewExt,_} = textual_order(Ext,Fun,IxR),
+ {{NewRoot,NewExt},dummy};
+textual_order({Root1,Ext,Root2},Fun,IxIn) ->
+ {NewRoot1,IxR} = textual_order(Root1,Fun,IxIn),
+ {NewExt,IxE} = textual_order(Ext,Fun,IxR),
+ {NewRoot2,_} = textual_order(Root2,Fun,IxE),
+ {{NewRoot1,NewExt,NewRoot2},dummy}.
+
+extension(Components) when is_list(Components) ->
+ {Components,noext,[]};
+extension({Root,ExtList}) ->
+ ToOpt = fun(mandatory) ->
+ 'OPTIONAL';
+ (X) -> X
+ end,
+ {Root, [X#'ComponentType'{prop=ToOpt(Y)}||
+ X = #'ComponentType'{prop=Y}<-ExtList],[]};
+extension({Root1,ExtList,Root2}) ->
+ ToOpt = fun(mandatory) ->
+ 'OPTIONAL';
+ (X) -> X
+ end,
+ {Root1, [X#'ComponentType'{prop=ToOpt(Y)}||
+ X = #'ComponentType'{prop=Y}<-ExtList], Root2}.
+
+check_setof(S,Type,Component) when is_record(Component,type) ->
+ check_type(S,Type,Component).
+
+check_selectiontype(S,Name,#type{def=Eref})
+ when is_record(Eref,'Externaltypereference') ->
+ {RefMod,TypeDef} = get_referenced_type(S,Eref),
+ NewS = S#state{module=load_asn1_module(S,RefMod),
+ mname=RefMod,
+ type=TypeDef,
+ tname=get_datastr_name(TypeDef)},
+ check_selectiontype2(NewS,Name,TypeDef);
+check_selectiontype(S,Name,Type=#type{def={pt,_,_}}) ->
+ TName =
+ case S#state.recordtopname of
+ [] ->
+ S#state.tname;
+ N -> N
+ end,
+ TDef = #typedef{name=TName,typespec=Type},
+ check_selectiontype2(S,Name,TDef);
+check_selectiontype(S,Name,Type) ->
+ Msg = lists:flatten(io_lib:format("SelectionType error: ~w < ~w must be a reference to a CHOICE.",[Name,Type])),
+ error({type,Msg,S}).
+
+check_selectiontype2(S,Name,TypeDef) ->
+ NewS = S#state{recordtopname=get_datastr_name(TypeDef)},
+ CheckedType = check_type(NewS,TypeDef,TypeDef#typedef.typespec),
+ Components = get_choice_components(S,CheckedType#type.def),
+ case lists:keysearch(Name,#'ComponentType'.name,Components) of
+ {value,C} ->
+ %% The selected type will have the tag of the selected type.
+ _T = C#'ComponentType'.typespec;
+% T#type{tag=def_to_tag(NewS,T#type.def)};
+ _ ->
+ Msg = lists:flatten(io_lib:format("error checking SelectionType: ~w~n",[Name])),
+ error({type,Msg,S})
+ end.
+
+
+check_restrictedstring(_S,_Def,_Constr) ->
+ ok.
+
+check_objectidentifier(_S,_Constr) ->
+ ok.
+
+check_relative_oid(_S,_Constr) ->
+ ok.
+% check all aspects of a CHOICE
+% - that all alternative names are unique
+% - that all TAGS are ok (when TAG default is applied)
+% - that each alternative is of a valid type
+% - that the extension marks are valid
+check_choice(S,Type,Components) when is_list(Components) ->
+ case check_unique([C||C <- Components,
+ is_record(C,'ComponentType')],#'ComponentType'.name) of
+ [] ->
+ %% sort_canonical(Components),
+ Components2 = maybe_automatic_tags(S,Components),
+ %NewComps =
+ case check_each_alternative(S,Type,Components2) of
+ {NewComponents,NewEcomps} ->
+ check_unique_tags(S,NewComponents ++ NewEcomps),
+ {NewComponents,NewEcomps};
+ NewComponents ->
+ check_unique_tags(S,NewComponents),
+ NewComponents
+ end;
+
+ Dupl ->
+ throw({error,{asn1,{duplicate_choice_alternatives,Dupl}}})
+ end;
+check_choice(_S,_,[]) ->
+ [].
+
+maybe_automatic_tags(S,C) ->
+ TagNos = tag_nums(C),
+ case (S#state.module)#module.tagdefault of
+ 'AUTOMATIC' ->
+ generate_automatic_tags(S,C,TagNos);
+ _ ->
+ %% maybe is the module a multi file module were only some of
+ %% the modules have defaulttag AUTOMATIC TAGS then the names
+ %% of those types are saved in the table automatic_tags
+ Name= S#state.tname,
+ case is_automatic_tagged_in_multi_file(Name) of
+ true ->
+ generate_automatic_tags(S,C,TagNos);
+ false ->
+ C
+ end
+ end.
+
+%% Pos == 1 for Root1, 2 for Ext, 3 for Root2
+tag_nums(Cl) ->
+ tag_nums(Cl,0,0).
+tag_nums([{'EXTENSIONMARK',_,_}|Rest],Ext,Root2) ->
+ tag_nums_ext(Rest,Ext,Root2);
+tag_nums([_|Rest],Ext,Root2) ->
+ tag_nums(Rest,Ext+1,Root2+1);
+tag_nums([],Ext,Root2) ->
+ [0,Ext,Root2].
+tag_nums_ext([{'EXTENSIONMARK',_,_}|Rest],Ext,Root2) ->
+ tag_nums_root2(Rest,Ext,Root2);
+tag_nums_ext([_|Rest],Ext,Root2) ->
+ tag_nums_ext(Rest,Ext,Root2);
+tag_nums_ext([],Ext,_Root2) ->
+ [0,Ext,0].
+tag_nums_root2([_|Rest],Ext,Root2) ->
+ tag_nums_root2(Rest,Ext+1,Root2);
+tag_nums_root2([],Ext,Root2) ->
+ [0,Ext,Root2].
+
+is_automatic_tagged_in_multi_file(Name) ->
+ case ets:info(automatic_tags) of
+ undefined ->
+ %% this case when not multifile compilation
+ false;
+ _ ->
+% case ets:member(automatic_tags,Name) of
+ case ets:lookup(automatic_tags,Name) of
+% true ->
+% true;
+% _ ->
+% false
+ [] -> false;
+ _ -> true
+ end
+ end.
+
+generate_automatic_tags(_S,C,TagNo) ->
+ case any_manual_tag(C) of
+ true ->
+ C;
+ false ->
+ generate_automatic_tags1(C,TagNo)
+ end.
+
+generate_automatic_tags1([H|T],[TagNo|TagNos]) when is_record(H,'ComponentType') ->
+ #'ComponentType'{typespec=Ts} = H,
+ NewTs = Ts#type{tag=[#tag{class='CONTEXT',
+ number=TagNo,
+ type={default,'IMPLICIT'},
+ form= 0 }]}, % PRIMITIVE
+ [H#'ComponentType'{typespec=NewTs}|generate_automatic_tags1(T,[TagNo+1|TagNos])];
+generate_automatic_tags1([ExtMark|T],[_TagNo|TagNos]) -> % EXTENSIONMARK
+ [ExtMark | generate_automatic_tags1(T,TagNos)];
+generate_automatic_tags1([],_) ->
+ [].
+
+any_manual_tag([#'ComponentType'{typespec=#type{tag=[]}}|Rest]) ->
+ any_manual_tag(Rest);
+any_manual_tag([{'EXTENSIONMARK',_,_}|Rest]) ->
+ any_manual_tag(Rest);
+any_manual_tag([_|_Rest]) ->
+ true;
+any_manual_tag([]) ->
+ false.
+
+
+check_unique_tags(S,C) ->
+ case (S#state.module)#module.tagdefault of
+ 'AUTOMATIC' ->
+ case any_manual_tag(C) of
+ false -> true;
+ _ -> collect_and_sort_tags(C,[])
+ end;
+ _ ->
+ collect_and_sort_tags(C,[])
+ end.
+
+collect_and_sort_tags([C|Rest],Acc) when is_record(C,'ComponentType') ->
+ collect_and_sort_tags(Rest,C#'ComponentType'.tags ++ Acc);
+collect_and_sort_tags([_|Rest],Acc) ->
+ collect_and_sort_tags(Rest,Acc);
+collect_and_sort_tags([],Acc) ->
+ {Dupl,_}= lists:mapfoldl(fun(El,El)->{{dup,El},El};(El,_Prev)-> {El,El} end,notag,lists:sort(Acc)),
+ Dupl2 = [Dup|| {dup,Dup} <- Dupl],
+ if
+ length(Dupl2) > 0 ->
+ throw({error,{asn1,{duplicates_of_the_tags,Dupl2}}});
+ true ->
+ true
+ end.
+
+check_unique(L,Pos) ->
+ Slist = lists:keysort(Pos,L),
+ check_unique2(Slist,Pos,[]).
+
+check_unique2([A,B|T],Pos,Acc) when element(Pos,A) == element(Pos,B) ->
+ check_unique2([B|T],Pos,[element(Pos,B)|Acc]);
+check_unique2([_|T],Pos,Acc) ->
+ check_unique2(T,Pos,Acc);
+check_unique2([],_,Acc) ->
+ lists:reverse(Acc).
+
+check_each_component(S,Type,Components) ->
+ check_each_component(S,Type,Components,[],[],[],root1).
+
+check_each_component(S = #state{abscomppath=Path,recordtopname=TopName},Type,
+ [C|Ct],Acc,Extacc,Acc2,Ext) when is_record(C,'ComponentType') ->
+ #'ComponentType'{name=Cname,typespec=Ts,prop=Prop} = C,
+ NewAbsCPath =
+ case Ts#type.def of
+ #'Externaltypereference'{} -> [];
+ _ -> [Cname|Path]
+ end,%%XXX Cname = 'per-message-indicators'
+ CheckedTs = check_type(S#state{abscomppath=NewAbsCPath,
+ recordtopname=[Cname|TopName]},Type,Ts),
+ NewTags = get_taglist(S,CheckedTs),
+
+ NewProp =
+ case normalize_value(S,CheckedTs,Prop,[Cname|TopName]) of
+ mandatory -> mandatory;
+ 'OPTIONAL' -> 'OPTIONAL';
+ DefaultValue -> {'DEFAULT',DefaultValue}
+ end,
+ NewC = C#'ComponentType'{typespec=CheckedTs,prop=NewProp,tags=NewTags},
+ case Ext of
+ root1 ->
+ check_each_component(S,Type,Ct,[NewC|Acc],Extacc,Acc2,Ext);
+ ext ->
+ check_each_component(S,Type,Ct,Acc,[NewC|Extacc],Acc2,Ext);
+ root2 ->
+ check_each_component(S,Type,Ct,Acc,Extacc,[NewC|Acc2],Ext)
+ end;
+check_each_component(S,Type,[_|Ct],Acc,Extacc,Acc2,root1) -> % skip 'EXTENSIONMARK'
+ check_each_component(S,Type,Ct,Acc,Extacc,Acc2,ext);
+check_each_component(S,Type,[_|Ct],Acc,Extacc,Acc2,ext) -> % skip 'EXTENSIONMARK'
+ check_each_component(S,Type,Ct,Acc,Extacc,Acc2,root2);
+check_each_component(_S,_,[_C|_Ct],_,_,_,root2) -> % 'EXTENSIONMARK'
+ throw({error,{asn1,{too_many_extension_marks}}});
+check_each_component(_S,_,[],Acc,Extacc,_,ext) ->
+ {lists:reverse(Acc),lists:reverse(Extacc)};
+check_each_component(_S,_,[],Acc1,ExtAcc,Acc2,root2) ->
+ {lists:reverse(Acc1),lists:reverse(ExtAcc),lists:reverse(Acc2)};
+check_each_component(_S,_,[],Acc,_,_,root1) ->
+ lists:reverse(Acc).
+
+%% check_each_alternative(S,Type,{Rlist,ExtList}) ->
+%% {check_each_alternative(S,Type,Rlist),
+%% check_each_alternative(S,Type,ExtList)};
+check_each_alternative(S,Type,[C|Ct]) ->
+ check_each_alternative(S,Type,[C|Ct],[],[],noext).
+
+check_each_alternative(S=#state{abscomppath=Path,recordtopname=TopName},Type,[C|Ct],
+ Acc,Extacc,Ext) when is_record(C,'ComponentType') ->
+ #'ComponentType'{name=Cname,typespec=Ts,prop=_Prop} = C,
+ NewAbsCPath =
+ case Ts#type.def of
+ #'Externaltypereference'{} -> [];
+ _ -> [Cname|Path]
+ end,
+ NewState =
+ S#state{abscomppath=NewAbsCPath,recordtopname=[Cname|TopName]},
+ CheckedTs = check_type(NewState,Type,Ts),
+ NewTags = get_taglist(S,CheckedTs),
+ NewC = C#'ComponentType'{typespec=CheckedTs,tags=NewTags},
+ case Ext of
+ noext ->
+ check_each_alternative(S,Type,Ct,[NewC|Acc],Extacc,Ext);
+ ext ->
+ check_each_alternative(S,Type,Ct,Acc,[NewC|Extacc],Ext)
+ end;
+
+check_each_alternative(S,Type,[_|Ct],Acc,Extacc,noext) -> % skip 'EXTENSIONMARK'
+ check_each_alternative(S,Type,Ct,Acc,Extacc,ext);
+check_each_alternative(_S,_,[_C|_Ct],_,_,ext) -> % skip 'EXTENSIONMARK'
+ throw({error,{asn1,{too_many_extension_marks}}});
+check_each_alternative(_S,_,[],Acc,Extacc,ext) ->
+ {lists:reverse(Acc),lists:reverse(Extacc)};
+check_each_alternative(_S,_,[],Acc,_,noext) ->
+ lists:reverse(Acc).
+
+%% componentrelation_leadingattr/2 searches the structure for table
+%% constraints, if any is found componentrelation_leadingattr/5 is
+%% called.
+componentrelation_leadingattr(S,CompList) ->
+ Cs =
+ case CompList of
+ {Comp1, EComps, Comp2} ->
+ Comp1++EComps++Comp2;
+ {Components,EComponents} when is_list(Components) ->
+ Components ++ EComponents;
+ CompList when is_list(CompList) ->
+ CompList
+ end,
+
+ %% get_simple_table_if_used/2 should find out whether there are any
+ %% component relation constraints in the entire tree of Cs1 that
+ %% relates to this level. It returns information about the simple
+ %% table constraint necessary for the the call to
+ %% componentrelation_leadingattr/6. The step when the leading
+ %% attribute and the syntax tree is modified to support the code
+ %% generating.
+ case get_simple_table_if_used(S,Cs) of
+ [] -> {false,CompList};
+ STList ->
+ componentrelation_leadingattr(S,Cs,Cs,STList,[],[])
+ end.
+
+%% componentrelation_leadingattr/6 when all components are searched
+%% the new modified components are returned together with the "leading
+%% attribute" information, which later is stored in the tablecinf
+%% field in the SEQUENCE/SET record. The "leading attribute"
+%% information is used to generate the lookup in the object set
+%% table. The other information gathered in the #type.tablecinf field
+%% is used in code generating phase too, to recognice the proper
+%% components for "open type" encoding and to propagate the result of
+%% the object set lookup when needed.
+componentrelation_leadingattr(_,[],_CompList,_,[],NewCompList) ->
+ {false,lists:reverse(NewCompList)};
+componentrelation_leadingattr(_,[],_CompList,_,LeadingAttr,NewCompList) ->
+ {lists:last(LeadingAttr),lists:reverse(NewCompList)}; %send all info in Ts later
+componentrelation_leadingattr(S,[C|Cs],CompList,STList,Acc,CompAcc) ->
+ {LAAcc,NewC} =
+ case catch componentrelation1(S,C#'ComponentType'.typespec,
+ [C#'ComponentType'.name]) of
+ {'EXIT',_} ->
+ {[],C};
+ {CRI=[{_A1,_B1,_C1,_D1}|_Rest],NewTSpec} ->
+ %% {ObjectSet,AtPath,ClassDef,Path}
+ %% _A1 is a reference to the object set of the
+ %% component relation constraint.
+ %% _B1 is the path of names in the at-list of the
+ %% component relation constraint.
+ %% _C1 is the class definition of the
+ %% ObjectClassFieldType.
+ %% _D1 is the path of components that was traversed to
+ %% find this constraint.
+ case leading_attr_index(S,CompList,CRI,
+ lists:reverse(S#state.abscomppath),[]) of
+ [] ->
+ {[],C};
+ [{ObjSet,Attr,N,ClassDef,_Path,ValueIndex}|_NewRest] ->
+ OS = object_set_mod_name(S,ObjSet),
+ UniqueFieldName =
+ case (catch get_unique_fieldname(S,#classdef{typespec=ClassDef})) of
+ {error,'__undefined_',_} ->
+ no_unique;
+ {asn1,Msg,_} ->
+ error({type,Msg,S});
+ {'EXIT',Msg} ->
+ error({type,{internal_error,Msg},S});
+ {Other,_} -> Other
+ end,
+% UsedFieldName = get_used_fieldname(S,Attr,STList),
+ %% Res should be done differently: even though
+ %% a unique field name exists it is not
+ %% certain that the ObjectClassFieldType of
+ %% the simple table constraint picks that
+ %% class field.
+ Res = #simpletableattributes{objectsetname=OS,
+%% c_name=asn1ct_gen:un_hyphen_var(Attr),
+ c_name=Attr,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex},
+ {[Res],C#'ComponentType'{typespec=NewTSpec}}
+ end;
+ _ ->
+ %% no constraint was found
+ {[],C}
+ end,
+ componentrelation_leadingattr(S,Cs,CompList,STList,LAAcc++Acc,
+ [NewC|CompAcc]).
+
+object_set_mod_name(_S,ObjSet) when is_atom(ObjSet) ->
+ ObjSet;
+object_set_mod_name(#state{mname=M},
+ #'Externaltypereference'{module=M,type=T}) ->
+ {M,T};
+object_set_mod_name(S,#'Externaltypereference'{module=M,type=T}) ->
+ case lists:member(M,S#state.inputmodules) of
+ true ->
+ T;
+ false ->
+ {M,T}
+ end.
+
+
+%% get_simple_table_if_used/2 searches the structure of Cs for any
+%% component relation constraints due to the present level of the
+%% structure. If there are any, the necessary information for code
+%% generation of the look up functionality in the object set table are
+%% returned.
+get_simple_table_if_used(S,Cs) ->
+ CNames = lists:map(fun(#'ComponentType'{name=Name}) -> Name;
+ (_) -> [] %% in case of extension marks
+ end,
+ Cs),
+ RefedSimpleTable=any_component_relation(S,Cs,CNames,[],[]),
+ get_simple_table_info(S,Cs,remove_doubles(RefedSimpleTable)).
+
+remove_doubles(L) ->
+ remove_doubles(L,[]).
+remove_doubles([H|T],Acc) ->
+ NewT = remove_doubles1(H,T),
+ remove_doubles(NewT,[H|Acc]);
+remove_doubles([],Acc) ->
+ Acc.
+
+remove_doubles1(El,L) ->
+ case lists:delete(El,L) of
+ L -> L;
+ NewL -> remove_doubles1(El,NewL)
+ end.
+
+%% get_simple_table_info searches the commponents Cs by the path from
+%% an at-list (third argument), and follows into a component of it if
+%% necessary, to get information needed for code generating.
+%%
+%% Returns a list of tuples with three elements. It holds a list of
+%% atoms that is the path, the name of the field of the class that are
+%% referred to in the ObjectClassFieldType, and the name of the unique
+%% field of the class of the ObjectClassFieldType.
+%%
+% %% The level information outermost/innermost must be kept. There are
+% %% at least two possibilities to cover here for an outermost case: 1)
+% %% Both the simple table and the component relation have a common path
+% %% at least one step below the outermost level, i.e. the leading
+% %% information shall be on a sub level. 2) They don't have any common
+% %% path.
+get_simple_table_info(S,Cs,[AtList|Rest]) ->
+ [get_simple_table_info1(S,Cs,AtList,[])|get_simple_table_info(S,Cs,Rest)];
+get_simple_table_info(_,_,[]) ->
+ [].
+get_simple_table_info1(S,Cs,[Cname|Cnames],Path) when is_list(Cs) ->
+ case lists:keysearch(Cname,#'ComponentType'.name,Cs) of
+ {value,C} ->
+ get_simple_table_info1(S,C,Cnames,[Cname|Path]);
+ _ ->
+ error({type,"Missing expected simple table constraint",S})
+ end;
+get_simple_table_info1(S,#'ComponentType'{typespec=TS},[],Path) ->
+ %% In this component there must be a simple table constraint
+ %% o.w. the asn1 code is wrong.
+ #type{def=OCFT,constraint=Cnstr} = TS,
+ case constraint_member(simpletable,Cnstr) of
+ {true,{simpletable,_OSRef}} ->
+ simple_table_info(S,OCFT,Path);
+ _ ->
+ error({type,{"missing expected simple table constraint",
+ Cnstr},S})
+ end;
+get_simple_table_info1(S,#'ComponentType'{typespec=TS},Cnames,Path) ->
+ Components = get_atlist_components(TS#type.def),
+ get_simple_table_info1(S,Components,Cnames,Path).
+
+
+simple_table_info(S,#'ObjectClassFieldType'{classname=ClRef,
+ class=ObjectClass,
+ fieldname=FieldName},Path) ->
+
+ ObjectClassFieldName =
+ case FieldName of
+ {LastFieldName,[]} -> LastFieldName;
+ {_FirstFieldName,FieldNames} ->
+ lists:last(FieldNames)
+ end,
+ %%ObjectClassFieldName is the last element in the dotted
+ %%list of the ObjectClassFieldType. The last element may
+ %%be of another class, that is referenced from the class
+ %%of the ObjectClassFieldType
+ ClassDef =
+ case ObjectClass of
+ [] ->
+ {_,CDef}=get_referenced_type(S,ClRef),
+ CDef;
+ _ -> #classdef{typespec=ObjectClass}
+ end,
+ UniqueName =
+ case (catch get_unique_fieldname(S,ClassDef)) of
+ {error,'__undefined_',_} -> no_unique;
+ {asn1,Msg,_} ->
+ error({type,Msg,S});
+ {'EXIT',Msg} ->
+ error({type,{internal_error,Msg},S});
+ {Other,_} -> Other
+ end,
+ {lists:reverse(Path),ObjectClassFieldName,UniqueName};
+simple_table_info(S,Type,_) ->
+ error({type,{"the type referenced by a componentrelation constraint must be a ObjectClassFieldType",Type},S}).
+
+
+%% any_component_relation searches for all component relation
+%% constraints that refers to the actual level and returns a list of
+%% the "name path" in the at-list to the component relation constraint
+%% that must refer to a simple table constraint. The list is empty if
+%% no component relation constraints were found.
+%%
+%% NamePath has the names of all components that are followed from the
+%% beginning of the search. CNames holds the names of all components
+%% of the start level, this info is used if an outermost at-notation
+%% is found to check the validity of the at-list.
+any_component_relation(S,[C|Cs],CNames,NamePath,Acc) ->
+ CName = C#'ComponentType'.name,
+ Type = C#'ComponentType'.typespec,
+ CRelPath =
+ case constraint_member(componentrelation,Type#type.constraint) of
+%% [{componentrelation,_,AtNotation}] ->
+ {true,{_,_,AtNotation}} ->
+ %% Found component relation constraint, now check
+ %% whether this constraint is relevant for the level
+ %% where the search started
+ AtNot = extract_at_notation(AtNotation),
+ %% evaluate_atpath returns the relative path to the
+ %% simple table constraint from where the component
+ %% relation is found.
+ evaluate_atpath(S,NamePath,CNames,AtNot);
+ _ ->
+ []
+ end,
+ InnerAcc =
+ case {Type#type.inlined,
+ asn1ct_gen:type(asn1ct_gen:get_inner(Type#type.def))} of
+ {no,{constructed,bif}} ->
+ {InnerCs,NewNamePath} =
+ case get_components(Type#type.def) of
+ {IC1,_IC2} -> {IC1 ++ IC1,[CName|NamePath]};
+ T when is_record(T,type) -> {T,NamePath};
+ IC -> {IC,[CName|NamePath]}
+ end,
+ %% here we are interested in components of an
+ %% SEQUENCE/SET OF as well as SEQUENCE, SET and CHOICE
+ any_component_relation(S,InnerCs,CNames,NewNamePath,[]);
+ _ ->
+ []
+ end,
+ any_component_relation(S,Cs,CNames,NamePath,InnerAcc++CRelPath++Acc);
+any_component_relation(S,Type,CNames,NamePath,Acc) when is_record(Type,type) ->
+ CRelPath =
+ case constraint_member(componentrelation,Type#type.constraint) of
+ {true,{_,_,AtNotation}} ->
+ AtNot = extract_at_notation(AtNotation),
+ evaluate_atpath(S,NamePath,CNames,AtNot);
+ _ ->
+ []
+ end,
+ InnerAcc =
+ case {Type#type.inlined,
+ asn1ct_gen:type(asn1ct_gen:get_inner(Type#type.def))} of
+ {no,{constructed,bif}} ->
+ InnerCs =
+ case get_components(Type#type.def) of
+ {IC1,_IC2} -> IC1 ++ IC1;
+ IC -> IC
+ end,
+ any_component_relation(S,InnerCs,CNames,NamePath,[]);
+ _ ->
+ []
+ end,
+ InnerAcc ++ CRelPath ++ Acc;
+any_component_relation(_,[],_,_,Acc) ->
+ Acc.
+
+constraint_member(componentrelation,[CRel={componentrelation,_,_}|_Rest]) ->
+ {true,CRel};
+constraint_member(simpletable,[ST={simpletable,_}|_Rest]) ->
+ {true,ST};
+constraint_member(Key,[_H|T]) ->
+ constraint_member(Key,T);
+constraint_member(_,[]) ->
+ false.
+
+%% evaluate_atpath/4 finds out whether the at notation refers to the
+%% search level. The list of referenced names in the AtNot list shall
+%% begin with a name that exists on the level it refers to. If the
+%% found AtPath is refering to the same sub-branch as the simple table
+%% has, then there shall not be any leading attribute info on this
+%% level.
+evaluate_atpath(_,[],Cnames,{innermost,AtPath=[Ref|_Refs]}) ->
+ %% any innermost constraint found deeper in the structure is
+ %% ignored.
+ case lists:member(Ref,Cnames) of
+ true -> [AtPath];
+ false -> []
+ end;
+%% In this case must check that the AtPath doesn't step any step of
+%% the NamePath, in that case the constraint will be handled in an
+%% inner level.
+evaluate_atpath(S=#state{abscomppath=TopPath},NamePath,Cnames,{outermost,AtPath=[_Ref|_Refs]}) ->
+ AtPathBelowTop =
+ case TopPath of
+ [] -> AtPath;
+ _ ->
+ case lists:prefix(TopPath,AtPath) of
+ true ->
+ lists:subtract(AtPath,TopPath);
+ _ -> []
+ end
+ end,
+ case {NamePath,AtPathBelowTop} of
+ {[H|_T1],[H|_T2]} -> []; % this must be handled in lower level
+ {_,[]} -> [];% this must be handled in an above level
+ {_,[H|_T]} ->
+ case lists:member(H,Cnames) of
+ true -> [AtPathBelowTop];
+ _ ->
+ %% error({type,{asn1,"failed to analyze at-path",AtPath},S})
+ throw({type,{asn1,"failed to analyze at-path",AtPath},S})
+ end
+ end;
+evaluate_atpath(_,_,_,_) ->
+ [].
+
+%% Type may be any of SEQUENCE, SET, CHOICE, SEQUENCE OF, SET OF but
+%% only the three first have valid components.
+get_atlist_components(Def) ->
+ get_components(atlist,Def).
+
+get_components(Def) ->
+ get_components(any,Def).
+
+get_components(_,#'SEQUENCE'{components=Cs}) ->
+ Cs;
+get_components(_,#'SET'{components=Cs}) ->
+ Cs;
+get_components(_,{'CHOICE',Cs}) ->
+ Cs;
+%do not step in inlined structures
+get_components(any,{'SEQUENCE OF',T = #type{def=_Def,inlined=no}}) ->
+% get_components(any,Def);
+ T;
+get_components(any,{'SET OF',T = #type{def=_Def,inlined=no}}) ->
+% get_components(any,Def);
+ T;
+get_components(_,_) ->
+ [].
+
+get_choice_components(_S,{'CHOICE',Components}) when is_list(Components)->
+ Components;
+get_choice_components(_S,{'CHOICE',{C1,C2}}) when is_list(C1),is_list(C2) ->
+ C1++C2;
+get_choice_components(S,ERef=#'Externaltypereference'{}) ->
+ {_RefMod,TypeDef}=get_referenced_type(S,ERef),
+ #typedef{typespec=TS} = TypeDef,
+ get_choice_components(S,TS#type.def).
+
+extract_at_notation([{Level,[#'Externalvaluereference'{value=Name}|Rest]}]) ->
+ {Level,[Name|extract_at_notation1(Rest)]};
+extract_at_notation(At) ->
+ exit({error,{asn1,{at_notation,At}}}).
+extract_at_notation1([#'Externalvaluereference'{value=Name}|Rest]) ->
+ [Name|extract_at_notation1(Rest)];
+extract_at_notation1([]) ->
+ [].
+
+%% componentrelation1/1 identifies all componentrelation constraints
+%% that exist in C or in the substructure of C. Info about the found
+%% constraints are returned in a list. It is ObjectSet, the reference
+%% to the object set, AttrPath, the name atoms extracted from the
+%% at-list in the component relation constraint, ClassDef, the
+%% objectclass record of the class of the ObjectClassFieldType, Path,
+%% that is the component name "path" from the searched level to this
+%% constraint.
+%%
+%% The function is called with one component of the type in turn and
+%% with the component name in Path at the first call. When called from
+%% within, the name of the inner component is added to Path.
+componentrelation1(S,C = #type{def=Def,constraint=Constraint,tablecinf=TCI},
+ Path) ->
+ Ret =
+% case Constraint of
+% [{componentrelation,{_,_,ObjectSet},AtList}|_Rest] ->
+ case constraint_member(componentrelation,Constraint) of
+ {true,{_,{_,_,ObjectSet},AtList}} ->
+ [{_,AL=[#'Externalvaluereference'{}|_R1]}|_R2] = AtList,
+ %% Note: if Path is longer than one,i.e. it is within
+ %% an inner type of the actual level, then the only
+ %% relevant at-list is of "outermost" type.
+%% #'ObjectClassFieldType'{class=ClassDef} = Def,
+ ClassDef = get_ObjectClassFieldType_classdef(S,Def),
+ AtPath =
+ lists:map(fun(#'Externalvaluereference'{value=V})->V end,
+ AL),
+ {[{ObjectSet,AtPath,ClassDef,Path}],Def};
+ _ ->
+ %% check the inner type of component
+ innertype_comprel(S,Def,Path)
+ end,
+ case Ret of
+ nofunobj ->
+ nofunobj; %% ignored by caller
+ {CRelI=[{ObjSet,_,_,_}],NewDef} -> %%
+ TCItmp = lists:subtract(TCI,[{objfun,ObjSet}]),
+ {CRelI,C#type{tablecinf=[{objfun,ObjSet}|TCItmp],def=NewDef}};
+ {CompRelInf,NewDef} -> %% more than one tuple in CompRelInf
+ TCItmp = lists:subtract(TCI,[{objfun,anyset}]),
+ {CompRelInf,C#type{tablecinf=[{objfun,anyset}|TCItmp],def=NewDef}}
+ end.
+
+innertype_comprel(S,{'SEQUENCE OF',Type},Path) ->
+ case innertype_comprel1(S,Type,Path) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewType} ->
+ {CompRelInf,{'SEQUENCE OF',NewType}}
+ end;
+innertype_comprel(S,{'SET OF',Type},Path) ->
+ case innertype_comprel1(S,Type,Path) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewType} ->
+ {CompRelInf,{'SET OF',NewType}}
+ end;
+innertype_comprel(S,{'CHOICE',CTypeList},Path) ->
+ case componentlist_comprel(S,CTypeList,[],Path,[]) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewCs} ->
+ {CompRelInf,{'CHOICE',NewCs}}
+ end;
+innertype_comprel(S,Seq = #'SEQUENCE'{components=Cs},Path) ->
+ case componentlist_comprel(S,Cs,[],Path,[]) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewCs} ->
+ {CompRelInf,Seq#'SEQUENCE'{components=NewCs}}
+ end;
+innertype_comprel(S,Set = #'SET'{components=Cs},Path) ->
+ case componentlist_comprel(S,Cs,[],Path,[]) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewCs} ->
+ {CompRelInf,Set#'SET'{components=NewCs}}
+ end;
+innertype_comprel(_,_,_) ->
+ nofunobj.
+
+componentlist_comprel(S,[C = #'ComponentType'{name=Name,typespec=Type}|Cs],
+ Acc,Path,NewCL) ->
+ case catch componentrelation1(S,Type,Path++[Name]) of
+ {'EXIT',_} ->
+ componentlist_comprel(S,Cs,Acc,Path,[C|NewCL]);
+ nofunobj ->
+ componentlist_comprel(S,Cs,Acc,Path,[C|NewCL]);
+ {CRelInf,NewType} ->
+ componentlist_comprel(S,Cs,CRelInf++Acc,Path,
+ [C#'ComponentType'{typespec=NewType}|NewCL])
+ end;
+componentlist_comprel(_,[],Acc,_,NewCL) ->
+ case Acc of
+ [] ->
+ nofunobj;
+ _ ->
+ {Acc,lists:reverse(NewCL)}
+ end.
+
+innertype_comprel1(S,T = #type{def=Def,constraint=Cons,tablecinf=TCI},Path) ->
+ Ret =
+% case Cons of
+% [{componentrelation,{_,_,ObjectSet},AtList}|_Rest] ->
+ case constraint_member(componentrelation,Cons) of
+ {true,{_,{_,_,ObjectSet},AtList}} ->
+ %% This AtList must have an "outermost" at sign to be
+ %% relevent here.
+ [{_,AL=[#'Externalvaluereference'{value=_Attr}|_R1]}|_R2]
+ = AtList,
+%% #'ObjectClassFieldType'{class=ClassDef} = Def,
+ ClassDef = get_ObjectClassFieldType_classdef(S,Def),
+ AtPath =
+ lists:map(fun(#'Externalvaluereference'{value=V})->V end,
+ AL),
+ [{ObjectSet,AtPath,ClassDef,Path}];
+ _ ->
+ innertype_comprel(S,Def,Path)
+ end,
+ case Ret of
+ nofunobj -> nofunobj;
+ L = [{ObjSet,_,_,_}] ->
+ TCItmp = lists:subtract(TCI,[{objfun,ObjSet}]),
+ {L,T#type{tablecinf=[{objfun,ObjSet}|TCItmp]}};
+ {CRelInf,NewDef} ->
+ TCItmp = lists:subtract(TCI,[{objfun,anyset}]),
+ {CRelInf,T#type{def=NewDef,tablecinf=[{objfun,anyset}|TCItmp]}}
+ end.
+
+
+%% leading_attr_index counts the index and picks the name of the
+%% component that is at the actual level in the at-list of the
+%% component relation constraint (AttrP). AbsP is the path of
+%% component names from the top type level to the actual level. AttrP
+%% is a list with the atoms from the at-list.
+leading_attr_index(S,Cs,[H={_,AttrP,_,_}|T],AbsP,Acc) ->
+ AttrInfo =
+ case lists:prefix(AbsP,AttrP) of
+ %% why this ?? It is necessary when in same situation as
+ %% TConstrChoice, there is an inner structure with an
+ %% outermost at-list and the "leading attribute" code gen
+ %% may be at a level some steps below the outermost level.
+ true ->
+ RelativAttrP = lists:subtract(AttrP,AbsP),
+ %% The header is used to calculate the index of the
+ %% component and to give the fun, received from the
+ %% object set look up, an unique name. The tail is
+ %% used to match the proper value input to the fun.
+ {hd(RelativAttrP),tl(RelativAttrP)};
+ false ->
+ {hd(AttrP),tl(AttrP)}
+ end,
+ case leading_attr_index1(S,Cs,H,AttrInfo,1) of
+ 0 ->
+ leading_attr_index(S,Cs,T,AbsP,Acc);
+ Res ->
+ leading_attr_index(S,Cs,T,AbsP,[Res|Acc])
+ end;
+leading_attr_index(_,_Cs,[],_,Acc) ->
+ lists:reverse(Acc).
+
+leading_attr_index1(_,[],_,_,_) ->
+ 0;
+leading_attr_index1(S,[C|Cs],Arg={ObjectSet,_,CDef,P},
+ AttrInfo={Attr,SubAttr},N) ->
+ case C#'ComponentType'.name of
+ Attr ->
+ ValueMatch = value_match(S,C,Attr,SubAttr),
+ {ObjectSet,Attr,N,CDef,P,ValueMatch};
+ _ ->
+ leading_attr_index1(S,Cs,Arg,AttrInfo,N+1)
+ end.
+
+%% value_math gathers information for a proper value match in the
+%% generated encode function. For a SEQUENCE or a SET the index of the
+%% component is counted. For a CHOICE the index is 2.
+value_match(S,C,Name,SubAttr) ->
+ value_match(S,C,Name,SubAttr,[]). % C has name Name
+value_match(_S,#'ComponentType'{},_Name,[],Acc) ->
+ Acc;% do not reverse, indexes in reverse order
+value_match(S,#'ComponentType'{typespec=Type},Name,[At|Ats],Acc) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ Components =
+ case get_atlist_components(Type#type.def) of
+ [] -> error({type,{asn1,"element in at list must be a "
+ "SEQUENCE, SET or CHOICE.",Name},S});
+ Comps -> Comps
+ end,
+ {Index,ValueIndex} = component_value_index(S,InnerType,At,Components),
+ value_match(S,lists:nth(Index,Components),At,Ats,[ValueIndex|Acc]).
+
+component_value_index(S,'CHOICE',At,Components) ->
+ {component_index(S,At,Components),2};
+component_value_index(S,_,At,Components) ->
+ %% SEQUENCE or SET
+ Index = component_index(S,At,Components),
+ {Index,{Index+1,At}}.
+
+component_index(S,Name,Components) ->
+ component_index1(S,Name,Components,1).
+component_index1(_S,Name,[#'ComponentType'{name=Name}|_Cs],N) ->
+ N;
+component_index1(S,Name,[_C|Cs],N) ->
+ component_index1(S,Name,Cs,N+1);
+component_index1(S,Name,[],_) ->
+ error({type,{asn1,"component of at-list was not"
+ " found in substructure",Name},S}).
+
+get_unique_fieldname(_S,ClassDef) when is_record(ClassDef,classdef) ->
+%% {_,Fields,_} = ClassDef#classdef.typespec,
+ Fields = (ClassDef#classdef.typespec)#objectclass.fields,
+ get_unique_fieldname1(Fields,[]);
+get_unique_fieldname(S,#typedef{typespec=#type{def=ClassRef}}) ->
+ %% A class definition may be referenced as
+ %% REFED-CLASS ::= DEFINED-CLASS and then REFED-CLASS is a typedef
+ {_M,ClassDef} = get_referenced_type(S,ClassRef),
+ get_unique_fieldname(S,ClassDef).
+
+get_unique_fieldname1([],[]) ->
+ throw({error,'__undefined_',[]});
+get_unique_fieldname1([],[Name]) ->
+ Name;
+get_unique_fieldname1([],Acc) ->
+ throw({asn1,'only one UNIQUE field is allowed in CLASS',Acc});
+get_unique_fieldname1([{fixedtypevaluefield,Name,_,'UNIQUE',Opt}|Rest],Acc) ->
+ get_unique_fieldname1(Rest,[{Name,Opt}|Acc]);
+get_unique_fieldname1([_H|T],Acc) ->
+ get_unique_fieldname1(T,Acc).
+
+get_tableconstraint_info(S,Type,{CheckedTs,EComps,CheckedTs2}) ->
+ {get_tableconstraint_info(S,Type,CheckedTs,[]),
+ get_tableconstraint_info(S,Type,EComps,[]),
+ get_tableconstraint_info(S,Type,CheckedTs2,[])};
+get_tableconstraint_info(S,Type,{CheckedTs,EComps}) ->
+ {get_tableconstraint_info(S,Type,CheckedTs,[]),
+ get_tableconstraint_info(S,Type,EComps,[])};
+get_tableconstraint_info(S,Type,CheckedTs) ->
+ get_tableconstraint_info(S,Type,CheckedTs,[]).
+
+get_tableconstraint_info(_S,_Type,[],Acc) ->
+ lists:reverse(Acc);
+get_tableconstraint_info(S,Type,[C|Cs],Acc) ->
+ CheckedTs = C#'ComponentType'.typespec,
+ AccComp =
+ case CheckedTs#type.def of
+ %% ObjectClassFieldType
+ OCFT=#'ObjectClassFieldType'{} ->
+ NewOCFT =
+ OCFT#'ObjectClassFieldType'{class=[]},
+ C#'ComponentType'{typespec=
+ CheckedTs#type{
+ def=NewOCFT
+ }};
+% constraint=[{tableconstraint_info,
+% FieldRef}]}};
+ {'SEQUENCE OF',SOType} when is_record(SOType,type),
+ (element(1,SOType#type.def)=='CHOICE') ->
+ CTypeList = element(2,SOType#type.def),
+ NewInnerCList =
+ get_tableconstraint_info(S,Type,CTypeList),
+ C#'ComponentType'{typespec=
+ CheckedTs#type{
+ def={'SEQUENCE OF',
+ SOType#type{def={'CHOICE',
+ NewInnerCList}}}}};
+ {'SET OF',SOType} when is_record(SOType,type),
+ (element(1,SOType#type.def)=='CHOICE') ->
+ CTypeList = element(2,SOType#type.def),
+ NewInnerCList =
+ get_tableconstraint_info(S,Type,CTypeList),
+ C#'ComponentType'{typespec=
+ CheckedTs#type{
+ def={'SET OF',
+ SOType#type{def={'CHOICE',
+ NewInnerCList}}}}};
+ _ ->
+ C
+ end,
+ get_tableconstraint_info(S,Type,Cs,[AccComp|Acc]).
+
+get_referenced_fieldname([{_,FirstFieldname}]) ->
+ {FirstFieldname,[]};
+get_referenced_fieldname([{_,FirstFieldname}|Rest]) ->
+ {FirstFieldname,lists:map(fun(X)->element(2,X) end,Rest)};
+get_referenced_fieldname(Def={FieldName,RestFieldName}) when is_atom(FieldName),is_list(RestFieldName)->
+ Def;
+get_referenced_fieldname(Def) ->
+ {no_type,Def}.
+
+%% get_ObjectClassFieldType extracts the type from the chain of
+%% objects that leads to a final type.
+get_ObjectClassFieldType(S,ERef,PrimFieldNameList) when
+ is_record(ERef,'Externaltypereference') ->
+ {MName,Type} = get_referenced_type(S,ERef),
+ NewS = update_state(S#state{type=Type,
+ tname=ERef#'Externaltypereference'.type},MName),
+ ClassSpec = check_class(NewS,Type),
+ Fields = ClassSpec#objectclass.fields,
+ get_ObjectClassFieldType(S,Fields,PrimFieldNameList);
+get_ObjectClassFieldType(S,Fields,L=[_PrimFieldName1|_Rest]) ->
+ check_PrimitiveFieldNames(S,Fields,L),
+ get_OCFType(S,Fields,L);
+get_ObjectClassFieldType(S,ERef,{FieldName,Rest}) ->
+ get_ObjectClassFieldType(S,ERef,Rest ++ [FieldName]).
+
+check_PrimitiveFieldNames(_S,_Fields,_) ->
+ ok.
+
+%% get_ObjectClassFieldType_classdef gets the def of the class of the
+%% ObjectClassFieldType, i.e. the objectclass record. If the type has
+%% been checked (it may be a field type of an internal SEQUENCE) the
+%% class field = [], then the classdef has to be fetched by help of
+%% the class reference in the classname field.
+get_ObjectClassFieldType_classdef(S,#'ObjectClassFieldType'{classname=Name,class=[]}) ->
+ {_,#classdef{typespec=TS}} = get_referenced_type(S,Name),
+ TS;
+get_ObjectClassFieldType_classdef(_,#'ObjectClassFieldType'{class=Cl}) ->
+ Cl.
+
+get_OCFType(S,Fields,FieldnameList=[{_FieldType,_PrimFieldName}|_]) ->
+ get_OCFType(S,Fields,[PFN||{_,PFN} <- FieldnameList]);
+get_OCFType(S,Fields,[PrimFieldName|Rest]) ->
+ case lists:keysearch(PrimFieldName,2,Fields) of
+ {value,{fixedtypevaluefield,_,Type,_Unique,_OptSpec}} ->
+ {fixedtypevaluefield,PrimFieldName,Type};
+ {value,{objectfield,_,ClassRef,_Unique,_OptSpec}} ->
+ {MName,ClassDef} = get_referenced_type(S,ClassRef),
+ NewS = update_state(S#state{type=ClassDef,
+ tname=get_datastr_name(ClassDef)},
+ MName),
+ CheckedCDef = check_class(NewS,ClassDef),
+ get_OCFType(S,CheckedCDef#objectclass.fields,Rest);
+ {value,{objectsetfield,_,Type,_OptSpec}} ->
+ {MName,ClassDef} = get_referenced_type(S,Type#type.def),
+ NewS = update_state(S#state{type=ClassDef,
+ tname=get_datastr_name(ClassDef)},
+ MName),
+ CheckedCDef = check_class(NewS,ClassDef),
+ get_OCFType(S,CheckedCDef#objectclass.fields,Rest);
+
+ {value,Other} ->
+ {element(1,Other),PrimFieldName};
+ _ ->
+ throw({error,lists:flatten(io_lib:format("undefined FieldName in ObjectClassFieldType: ~w",[PrimFieldName]))})
+ end.
+
+get_taglist(S,Ext) when is_record(Ext,'Externaltypereference') ->
+ {_,T} = get_referenced_type(S,Ext),
+ get_taglist(S,T#typedef.typespec);
+get_taglist(S,Tref) when is_record(Tref,typereference) ->
+ {_,T} = get_referenced_type(S,Tref),
+ get_taglist(S,T#typedef.typespec);
+get_taglist(S,Type) when is_record(Type,type) ->
+ case Type#type.tag of
+ [] ->
+ get_taglist(S,Type#type.def);
+ [Tag|_] ->
+ [asn1ct_gen:def_to_tag(Tag)]
+ end;
+get_taglist(S,{'CHOICE',{Rc,Ec}}) ->
+ get_taglist(S,{'CHOICE',Rc ++ Ec});
+get_taglist(S,{'CHOICE',Components}) ->
+ get_taglist1(S,Components);
+%% ObjectClassFieldType OTP-4390
+get_taglist(_S,#'ObjectClassFieldType'{type={typefield,_}}) ->
+ [];
+get_taglist(S,#'ObjectClassFieldType'{type={fixedtypevaluefield,_,Type}}) ->
+ get_taglist(S,Type);
+get_taglist(S,{ERef=#'Externaltypereference'{},FieldNameList})
+ when is_list(FieldNameList) ->
+ case get_ObjectClassFieldType(S,ERef,FieldNameList) of
+ {fixedtypevaluefield,_,Type} -> get_taglist(S,Type);
+ {TypeFieldName,_} when is_atom(TypeFieldName) -> []%should check if allowed
+ end;
+get_taglist(S,{ObjCl,FieldNameList}) when is_record(ObjCl,objectclass),
+ is_list(FieldNameList) ->
+ case get_ObjectClassFieldType(S,ObjCl#objectclass.fields,FieldNameList) of
+ {fixedtypevaluefield,_,Type} -> get_taglist(S,Type);
+ {TypeFieldName,_} when is_atom(TypeFieldName) -> []%should check if allowed
+ end;
+get_taglist(S,Def) ->
+ case lists:member(S#state.erule,[ber_bin_v2]) of
+ false ->
+ case Def of
+ 'ASN1_OPEN_TYPE' -> % open_type has no UNIVERSAL tag as such
+ [];
+ _ ->
+ [asn1ct_gen:def_to_tag(Def)]
+ end;
+ _ ->
+ []
+ end.
+
+get_taglist1(S,[#'ComponentType'{name=_Cname,tags=TagL}|Rest]) when is_list(TagL) ->
+ %% tag_list has been here , just return TagL and continue with next alternative
+ TagL ++ get_taglist1(S,Rest);
+get_taglist1(S,[#'ComponentType'{typespec=Ts,tags=undefined}|Rest]) ->
+ get_taglist(S,Ts) ++ get_taglist1(S,Rest);
+get_taglist1(S,[_H|Rest]) -> % skip EXTENSIONMARK
+ get_taglist1(S,Rest);
+get_taglist1(_S,[]) ->
+ [].
+
+%% def_to_tag(S,Def) ->
+%% case asn1ct_gen:def_to_tag(Def) of
+%% {'UNIVERSAL',T} ->
+%% case asn1ct_gen:prim_bif(T) of
+%% true ->
+%% ?TAG_PRIMITIVE(tag_number(T));
+%% _ ->
+%% ?TAG_CONSTRUCTED(tag_number(T))
+%% end;
+%% _ -> []
+%% end.
+%% tag_number('BOOLEAN') -> 1;
+%% tag_number('INTEGER') -> 2;
+%% tag_number('BIT STRING') -> 3;
+%% tag_number('OCTET STRING') -> 4;
+%% tag_number('NULL') -> 5;
+%% tag_number('OBJECT IDENTIFIER') -> 6;
+%% tag_number('ObjectDescriptor') -> 7;
+%% tag_number('EXTERNAL') -> 8;
+%% tag_number('INSTANCE OF') -> 8;
+%% tag_number('REAL') -> 9;
+%% tag_number('ENUMERATED') -> 10;
+%% tag_number('EMBEDDED PDV') -> 11;
+%% tag_number('UTF8String') -> 12;
+%% %%tag_number('RELATIVE-OID') -> 13;
+%% tag_number('SEQUENCE') -> 16;
+%% tag_number('SEQUENCE OF') -> 16;
+%% tag_number('SET') -> 17;
+%% tag_number('SET OF') -> 17;
+%% tag_number('NumericString') -> 18;
+%% tag_number('PrintableString') -> 19;
+%% tag_number('TeletexString') -> 20;
+%% %%tag_number('T61String') -> 20;
+%% tag_number('VideotexString') -> 21;
+%% tag_number('IA5String') -> 22;
+%% tag_number('UTCTime') -> 23;
+%% tag_number('GeneralizedTime') -> 24;
+%% tag_number('GraphicString') -> 25;
+%% tag_number('VisibleString') -> 26;
+%% %%tag_number('ISO646String') -> 26;
+%% tag_number('GeneralString') -> 27;
+%% tag_number('UniversalString') -> 28;
+%% tag_number('CHARACTER STRING') -> 29;
+%% tag_number('BMPString') -> 30.
+
+
+dbget_ex(_S,Module,Key) ->
+ case asn1_db:dbget(Module,Key) of
+ undefined ->
+
+ throw({error,{asn1,{undefined,{Module,Key}}}}); % this is catched on toplevel type or value
+ T -> T
+ end.
+
+merge_tags(T1, T2) when is_list(T2) ->
+ merge_tags2(T1 ++ T2, []);
+merge_tags(T1, T2) ->
+ merge_tags2(T1 ++ [T2], []).
+
+merge_tags2([T1= #tag{type='IMPLICIT'}, T2 |Rest], Acc) ->
+ merge_tags2([T1#tag{type=T2#tag.type, form=T2#tag.form}|Rest],Acc);
+merge_tags2([T1= #tag{type={default,'IMPLICIT'}}, T2 |Rest], Acc) ->
+ merge_tags2([T1#tag{type=T2#tag.type, form=T2#tag.form}|Rest],Acc);
+merge_tags2([H|T],Acc) ->
+ merge_tags2(T, [H|Acc]);
+merge_tags2([], Acc) ->
+ lists:reverse(Acc).
+
+%% merge_constraints(C1, []) ->
+%% C1;
+%% merge_constraints([], C2) ->
+%% C2;
+%% merge_constraints(C1, C2) ->
+%% {SList,VList,PAList,Rest} = splitlist(C1++C2,[],[],[],[]),
+%% SizeC = merge_constraints(SList),
+%% ValueC = merge_constraints(VList),
+%% PermAlphaC = merge_constraints(PAList),
+%% case Rest of
+%% [] ->
+%% SizeC ++ ValueC ++ PermAlphaC;
+%% _ ->
+%% throw({error,{asn1,{not_implemented,{merge_constraints,Rest}}}})
+%% end.
+
+%% merge_constraints([]) -> [];
+%% merge_constraints([C1 = {_,{Low1,High1}},{_,{Low2,High2}}|Rest]) when Low1 >= Low2,
+%% High1 =< High2 ->
+%% merge_constraints([C1|Rest]);
+%% merge_constraints([C1={'PermittedAlphabet',_},C2|Rest]) ->
+%% [C1|merge_constraints([C2|Rest])];
+%% merge_constraints([C1 = {_,{_Low1,_High1}},C2 = {_,{_Low2,_High2}}|_Rest]) ->
+%% throw({error,asn1,{conflicting_constraints,{C1,C2}}});
+%% merge_constraints([C]) ->
+%% [C].
+
+%% splitlist([C={'SizeConstraint',_}|Rest],Sacc,Vacc,PAacc,Restacc) ->
+%% splitlist(Rest,[C|Sacc],Vacc,PAacc,Restacc);
+%% splitlist([C={'ValueRange',_}|Rest],Sacc,Vacc,PAacc,Restacc) ->
+%% splitlist(Rest,Sacc,[C|Vacc],PAacc,Restacc);
+%% splitlist([C={'PermittedAlphabet',_}|Rest],Sacc,Vacc,PAacc,Restacc) ->
+%% splitlist(Rest,Sacc,Vacc,[C|PAacc],Restacc);
+%% splitlist([C|Rest],Sacc,Vacc,PAacc,Restacc) ->
+%% splitlist(Rest,Sacc,Vacc,PAacc,[C|Restacc]);
+%% splitlist([],Sacc,Vacc,PAacc,Restacc) ->
+%% {lists:reverse(Sacc),
+%% lists:reverse(Vacc),
+%% lists:reverse(PAacc),
+%% lists:reverse(Restacc)}.
+
+
+
+storeindb(S,M) when is_record(M,module) ->
+ TVlist = M#module.typeorval,
+ NewM = M#module{typeorval=findtypes_and_values(TVlist)},
+ asn1_db:dbnew(NewM#module.name),
+ asn1_db:dbput(NewM#module.name,'MODULE', NewM),
+ Res = storeindb(NewM#module.name,TVlist,[]),
+ include_default_class(S,NewM#module.name),
+ include_default_type(NewM#module.name),
+ Res.
+
+storeindb(Module,[H|T],ErrAcc) when is_record(H,typedef) ->
+ storeindb(Module,H#typedef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when is_record(H,valuedef) ->
+ storeindb(Module,H#valuedef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when is_record(H,ptypedef) ->
+ storeindb(Module,H#ptypedef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when is_record(H,classdef) ->
+ storeindb(Module,H#classdef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when is_record(H,pvaluesetdef) ->
+ storeindb(Module,H#pvaluesetdef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when is_record(H,pobjectdef) ->
+ storeindb(Module,H#pobjectdef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when is_record(H,pvaluedef) ->
+ storeindb(Module,H#pvaluedef.name,H,T,ErrAcc);
+storeindb(_,[],[]) -> ok;
+storeindb(_,[],ErrAcc) ->
+ {error,ErrAcc}.
+
+storeindb(Module,Name,H,T,ErrAcc) ->
+ case asn1_db:dbget(Module,Name) of
+ undefined ->
+ asn1_db:dbput(Module,Name,H),
+ storeindb(Module,T,ErrAcc);
+ _ ->
+ case H of
+ _Type when is_record(H,typedef) ->
+ error({type,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when is_record(H,valuedef) ->
+ error({value,"already defined",
+ #state{mname=Module,value=H,vname=Name}});
+ _Type when is_record(H,ptypedef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when is_record(H,pobjectdef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when is_record(H,pvaluesetdef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when is_record(H,pvaluedef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when is_record(H,classdef) ->
+ error({class,"already defined",
+ #state{mname=Module,value=H,vname=Name}})
+ end,
+ storeindb(Module,T,[H|ErrAcc])
+ end.
+
+findtypes_and_values(TVList) ->
+ findtypes_and_values(TVList,[],[],[],[],[],[]).%% Types,Values,
+%% Parameterizedtypes,Classes,Objects and ObjectSets
+
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,typedef),is_record(H#typedef.typespec,'Object') ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,[H#typedef.name|Oacc],OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,typedef),is_record(H#typedef.typespec,'ObjectSet') ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,Oacc,[H#typedef.name|OSacc]);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,typedef) ->
+ findtypes_and_values(T,[H#typedef.name|Tacc],Vacc,Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,valuedef) ->
+ findtypes_and_values(T,Tacc,[H#valuedef.name|Vacc],Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,ptypedef) ->
+ findtypes_and_values(T,Tacc,Vacc,[H#ptypedef.name|Pacc],Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,classdef) ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,[H#classdef.name|Cacc],Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,pvaluedef) ->
+ findtypes_and_values(T,Tacc,[H#pvaluedef.name|Vacc],Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,pvaluesetdef) ->
+ findtypes_and_values(T,Tacc,[H#pvaluesetdef.name|Vacc],Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,pobjectdef) ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,[H#pobjectdef.name|Oacc],OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when is_record(H,pobjectsetdef) ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,Oacc,[H#pobjectsetdef.name|OSacc]);
+findtypes_and_values([],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc) ->
+ {lists:reverse(Tacc),lists:reverse(Vacc),lists:reverse(Pacc),
+ lists:reverse(Cacc),lists:reverse(Oacc),lists:reverse(OSacc)}.
+
+
+
+error({export,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) ->
+ Pos = Ref#'Externaltypereference'.pos,
+ io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{export,Pos,Mname,Typename,Msg}};
+error({import,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) ->
+ PosOfDef =
+ fun(#'Externaltypereference'{pos=P}) -> P;
+ (#'Externalvaluereference'{pos=P}) -> P
+ end,
+ Pos = PosOfDef(Ref),
+ io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{import,Pos,Mname,Typename,Msg}};
+% error({type,{Msg1,Msg2},#state{mname=Mname,type=Type,tname=Typename}})
+% when is_record(Type,typedef) ->
+% io:format("asn1error:~p:~p:~p ~p~n",
+% [Type#typedef.pos,Mname,Typename,Msg1]),
+% {error,{type,Type#typedef.pos,Mname,Typename,Msg1,Msg2}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when is_record(Type,type) ->
+ io:format("asn1error:~p:~p~n~p~n",
+ [Mname,Typename,Msg]),
+ {error,{type,Mname,Typename,Msg}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when is_record(Type,typedef) ->
+ io:format("asn1error:~p:~p:~p~n~p~n",
+ [Type#typedef.pos,Mname,Typename,Msg]),
+ {error,{type,Type#typedef.pos,Mname,Typename,Msg}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when is_record(Type,ptypedef) ->
+ io:format("asn1error:~p:~p:~p~n~p~n",
+ [Type#ptypedef.pos,Mname,Typename,Msg]),
+ {error,{type,Type#ptypedef.pos,Mname,Typename,Msg}};
+error({type,Msg,#state{mname=Mname,value=Value,vname=Valuename}})
+ when is_record(Value,valuedef) ->
+ io:format("asn1error:~p:~p:~p~n~p~n",[Value#valuedef.pos,Mname,Valuename,Msg]),
+ {error,{type,Value#valuedef.pos,Mname,Valuename,Msg}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when is_record(Type,pobjectdef) ->
+ io:format("asn1error:~p:~p:~p~n~p~n",
+ [Type#pobjectdef.pos,Mname,Typename,Msg]),
+ {error,{type,Type#pobjectdef.pos,Mname,Typename,Msg}};
+error({value,Msg,#state{mname=Mname,value=Value,vname=Valuename}})
+ when is_record(Value,valuedef) ->
+ io:format("asn1error:~p:~p:~p~n~p~n",[Value#valuedef.pos,Mname,Valuename,Msg]),
+ {error,{value,Value#valuedef.pos,Mname,Valuename,Msg}};
+error({Other,Msg,#state{mname=Mname,value=#valuedef{pos=Pos},vname=Valuename}}) ->
+ io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Valuename,Msg]),
+ {error,{Other,Pos,Mname,Valuename,Msg}};
+error({Other,Msg,#state{mname=Mname,type=#typedef{pos=Pos},tname=Typename}}) ->
+ io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{Other,Pos,Mname,Typename,Msg}};
+error({Other,Msg,#state{mname=Mname,type=#classdef{pos=Pos},tname=Typename}}) ->
+ io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{Other,Pos,Mname,Typename,Msg}};
+error({Other,Msg,#state{mname=Mname,type=Type,tname=Typename}}) ->
+ io:format("asn1error:~p:~p:~p~n~p~n",[asn1ct:get_pos_of_def(Type),Mname,Typename,Msg]),
+ {error,{Other,asn1ct:get_pos_of_def(Type),Mname,Typename,Msg}}.
+
+include_default_type(Module) ->
+ NameAbsList = default_type_list(),
+ include_default_type1(Module,NameAbsList).
+
+include_default_type1(_,[]) ->
+ ok;
+include_default_type1(Module,[{Name,TS}|Rest]) ->
+ case asn1_db:dbget(Module,Name) of
+ undefined ->
+ T = #typedef{name=Name,
+ typespec=TS},
+ asn1_db:dbput(Module,Name,T);
+ _ -> ok
+ end,
+ include_default_type1(Module,Rest).
+
+default_type_list() ->
+ %% The EXTERNAL type is represented, according to ASN.1 1997,
+ %% as a SEQUENCE with components: identification, data-value-descriptor
+ %% and data-value.
+ Syntax =
+ #'ComponentType'{name=syntax,
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ Presentation_Cid =
+ #'ComponentType'{name='presentation-context-id',
+ typespec=#type{def='INTEGER'},
+ prop=mandatory},
+ Transfer_syntax =
+ #'ComponentType'{name='transfer-syntax',
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ Negotiation_items =
+ #type{def=
+ #'SEQUENCE'{components=
+ [Presentation_Cid,
+ Transfer_syntax#'ComponentType'{prop=mandatory}]}},
+ Context_negot =
+ #'ComponentType'{name='context-negotiation',
+ typespec=Negotiation_items,
+ prop=mandatory},
+
+ Data_value_descriptor =
+ #'ComponentType'{name='data-value-descriptor',
+ typespec=#type{def='ObjectDescriptor'},
+ prop='OPTIONAL'},
+ Data_value =
+ #'ComponentType'{name='data-value',
+ typespec=#type{def='OCTET STRING'},
+ prop=mandatory},
+
+ %% The EXTERNAL type is represented, according to ASN.1 1990,
+ %% as a SEQUENCE with components: direct-reference, indirect-reference,
+ %% data-value-descriptor and encoding.
+
+ Direct_reference =
+ #'ComponentType'{name='direct-reference',
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop='OPTIONAL',
+ tags=[{'UNIVERSAL',6}]},
+
+ Indirect_reference =
+ #'ComponentType'{name='indirect-reference',
+ typespec=#type{def='INTEGER'},
+ prop='OPTIONAL',
+ tags=[{'UNIVERSAL',2}]},
+
+ Single_ASN1_type =
+ #'ComponentType'{name='single-ASN1-type',
+ typespec=#type{tag=[{tag,'CONTEXT',0,
+ 'EXPLICIT',32}],
+ def='ANY'},
+ prop=mandatory,
+ tags=[{'CONTEXT',0}]},
+
+ Octet_aligned =
+ #'ComponentType'{name='octet-aligned',
+ typespec=#type{tag=[{tag,'CONTEXT',1,
+ 'IMPLICIT',0}],
+ def='OCTET STRING'},
+ prop=mandatory,
+ tags=[{'CONTEXT',1}]},
+
+ Arbitrary =
+ #'ComponentType'{name=arbitrary,
+ typespec=#type{tag=[{tag,'CONTEXT',2,
+ 'IMPLICIT',0}],
+ def={'BIT STRING',[]}},
+ prop=mandatory,
+ tags=[{'CONTEXT',2}]},
+
+ Encoding =
+ #'ComponentType'{name=encoding,
+ typespec=#type{def={'CHOICE',
+ [Single_ASN1_type,Octet_aligned,
+ Arbitrary]}},
+ prop=mandatory},
+
+ EXTERNAL_components1990 =
+ [Direct_reference,Indirect_reference,Data_value_descriptor,Encoding],
+
+ %% The EMBEDDED PDV type is represented by a SEQUENCE type
+ %% with components: identification and data-value
+ Abstract =
+ #'ComponentType'{name=abstract,
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ Transfer =
+ #'ComponentType'{name=transfer,
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ AbstractTrSeq =
+ #'SEQUENCE'{components=[Abstract,Transfer]},
+ Syntaxes =
+ #'ComponentType'{name=syntaxes,
+ typespec=#type{def=AbstractTrSeq},
+ prop=mandatory},
+ Fixed = #'ComponentType'{name=fixed,
+ typespec=#type{def='NULL'},
+ prop=mandatory},
+ Negotiations =
+ [Syntaxes,Syntax,Presentation_Cid,Context_negot,
+ Transfer_syntax,Fixed],
+ Identification2 =
+ #'ComponentType'{name=identification,
+ typespec=#type{def={'CHOICE',Negotiations}},
+ prop=mandatory},
+ EmbeddedPdv_components =
+ [Identification2,Data_value],
+
+ %% The CHARACTER STRING type is represented by a SEQUENCE type
+ %% with components: identification and string-value
+ String_value =
+ #'ComponentType'{name='string-value',
+ typespec=#type{def='OCTET STRING'},
+ prop=mandatory},
+ CharacterString_components =
+ [Identification2,String_value],
+
+ [{'EXTERNAL',
+ #type{tag=[#tag{class='UNIVERSAL',
+ number=8,
+ type='IMPLICIT',
+ form=32}],
+ def=#'SEQUENCE'{components=
+ EXTERNAL_components1990}}},
+ {'EMBEDDED PDV',
+ #type{tag=[#tag{class='UNIVERSAL',
+ number=11,
+ type='IMPLICIT',
+ form=32}],
+ def=#'SEQUENCE'{components=EmbeddedPdv_components}}},
+ {'CHARACTER STRING',
+ #type{tag=[#tag{class='UNIVERSAL',
+ number=29,
+ type='IMPLICIT',
+ form=32}],
+ def=#'SEQUENCE'{components=CharacterString_components}}}
+ ].
+
+
+include_default_class(S,Module) ->
+ NameAbsList = default_class_list(S),
+ include_default_class1(Module,NameAbsList).
+
+include_default_class1(_,[]) ->
+ ok;
+include_default_class1(Module,[{Name,TS}|Rest]) ->
+ case asn1_db:dbget(Module,Name) of
+ undefined ->
+ C = #classdef{checked=true,name=Name,
+ typespec=TS},
+ asn1_db:dbput(Module,Name,C);
+ _ -> ok
+ end,
+ include_default_class1(Module,Rest).
+
+default_class_list(S) ->
+ [{'TYPE-IDENTIFIER',
+ {objectclass,
+ [{fixedtypevaluefield,
+ id,
+ #type{tag=?TAG_PRIMITIVE(?N_OBJECT_IDENTIFIER),
+ def='OBJECT IDENTIFIER'},
+ 'UNIQUE',
+ 'MANDATORY'},
+ {typefield,'Type','MANDATORY'}],
+ {'WITH SYNTAX',
+ [{typefieldreference,'Type'},
+ 'IDENTIFIED',
+ 'BY',
+ {valuefieldreference,id}]}}},
+ {'ABSTRACT-SYNTAX',
+ {objectclass,
+ [{fixedtypevaluefield,
+ id,
+ #type{tag=?TAG_PRIMITIVE(?N_OBJECT_IDENTIFIER),
+ def='OBJECT IDENTIFIER'},
+ 'UNIQUE',
+ 'MANDATORY'},
+ {typefield,'Type','MANDATORY'},
+ {fixedtypevaluefield,
+ property,
+ #type{tag=?TAG_PRIMITIVE(?N_BIT_STRING),
+ def={'BIT STRING',[]}},
+ undefined,
+ {'DEFAULT',
+ [0,1,0]}}],
+ {'WITH SYNTAX',
+ [{typefieldreference,'Type'},
+ 'IDENTIFIED',
+ 'BY',
+ {valuefieldreference,id},
+ ['HAS',
+ 'PROPERTY',
+ {valuefieldreference,property}]]}}}].
+
+
+new_reference_name(Name) ->
+ case get(asn1_reference) of
+ undefined ->
+ put(asn1_reference,1),
+ list_to_atom(lists:concat([internal_,Name,"_",1]));
+ Num when is_integer(Num) ->
+ put(asn1_reference,Num+1),
+ list_to_atom(lists:concat([internal_,Name,"_",Num+1]))
+ end.
+
+get_record_prefix_name(S) ->
+ case lists:keysearch(record_name_prefix,1,S#state.options) of
+ {value,{_,Prefix}} ->
+ Prefix;
+ _ ->
+ ""
+ end.
+
+insert_once(S,Tab,Key) ->
+ case get(top_module) of
+ M when M == S#state.mname ->
+ asn1ct_gen:insert_once(Tab,Key),
+ ok;
+ _ ->
+ skipped
+ end.