本家「@IT」にはない内容をエンジニアライフで技術紹介するコラム。広く議論する場になることを目指します。

第059回_一般実体テーブル作成処理の実装3

»

前回の続きで、検討したVisitorを実装します。

GettingNameVisitorの実装

工夫の内容を元にNameノードから値を取得するGettingNameVisitorを実装します。

実装は次のようにできます。
public class GettingNameVisitor extends SyntaxParserVisitor
{
    private String m_name;
    
    public GettingNameVisitor()
    {
        m_name = null;
    }
    
    public String getName()
    {
        return m_name;
    }

    @Override
    public void visit(EbnfNameNode n)
    {
        Token token = m_tokenManager.nextToken();
        m_result = token.match(n);
        
        if(m_result)
        {
            //処理に成功したので巡回用データに登録する
            m_name = token.getString();
        }
        else
        {
            //何もしない
        }
    }
}

EntityValueVisitorの実装

CharRefVisitorとGettingNameVisitorの実装を元に EntityValueVisitorの実装は次のようになります。
public class EntityValueVisitor extends SyntaxParserVisitor
{
    private static EntityReferenceHelper m_EntityRefHelper
    = EntityReferenceHelper.getInstance();
    
    //PE実体のシンボルテーブルヘルパー
    private static PeReferenceHelper m_peRefHelper
    = PeReferenceHelper.getInstance();
    
    private String m_value;
    
    public EntityValueVisitor()
    {
        m_value = "";
    }
    
    public String getValue()
    {
        return m_value;
    }

    @Override
    public void visit(EbnfEvCharRefNode n)
    {
        CharRefVisitor v = new CharRefVisitor();
        
        v.visit(n);
        
        m_result = v.getResult();
        
        if(m_result)
        {
            m_value += v.getValue();
        }
        else
        {
            //何もしない
        }
    }
    
    
    @Override
    public void visit(EbnfNormalTokenB1Node n)
    {
        Token token = m_tokenManager.nextToken();
        m_result = token.match(n);
        
        if(m_result)
        {
            //巡回用データに文字列を追加する
            m_value +=token.getString();
        }
        else
        {
            //何もしない
        }
    }

    @Override
    public void visit(EbnfNormalTokenB2Node n)
    {
        Token token = m_tokenManager.nextToken();
        m_result = token.match(n);
        
        if(m_result)
        {
            //巡回用データに文字列を追加する
            m_value +=token.getString();
        }
        else
        {
            //何もしない
        }
    }
    
    @Override
    public void visit(EbnfEvEntityRefNode n)
    {
        GettingNameVisitor v = new GettingNameVisitor();

        v.visit(n);
        
        m_result = v.getResult();
        
        if(m_result)
        {
            String entityRefName = v.getName();
            
            //巡回用データに文字列を追加する
            m_value += m_EntityRefHelper.bypassInEntityValue(entityRefName);
        }
        else
        {
            //何もしない
        }
    }
    
    
    @Override
    public void visit(EbnfEvPEReferenceNode n)
    {
        GettingNameVisitor v = new GettingNameVisitor();

        v.visit(n);
        
        m_result = v.getResult();
        
        if(m_result)
        {
            String peRefName = v.getName();
            
            //置換文字列を取得する
            String text =
            m_peRefHelper.getReplacementTextInEntityValue(peRefName);
            
            //巡回用データに文字列を追加する
            m_value+=text;
        }
        else
        {
            //何もしない
        }
    }
}

EntityReferenceHelperの実装

EntityValueVisitorに出てきたEntityReferenceHelperは一般実体の参照の処理の違いを表現するクラスです。
EntityValueで一般実体参照を認識したときの処理
 2.一般実体の参照を認識したとき(修正なし)
 2-1.参照名を参照チェックテーブルに登録する
 2-2.バイパスする(何もしない)
を実装すると、次のようになります。
public class EntityReferenceHelper
{
    private static EntityReferenceHelper m_instance
                              = new EntityReferenceHelper();

    //実体の参照をチェックするテーブル
    private static EntityReferenceCheckSet m_checkTable
                     = EntityReferenceCheckSet.getInstance();
    
    private EntityReferenceHelper()
    {
    }
    
    public static EntityReferenceHelper getInstance()
    {
        return m_instance;
    }

    public String bypassInEntityValue(String refName)
    {
        //チェックテーブルに参照値を追加する
        m_checkTable.add(refName);
        
        return "&"+refName+";";
    }
}

EntityReferenceCheckSetの実装

EntityReferenceCheckSetは検討の中に出てきた一般実体の参照をチェックするテーブル(セット)です。登録部分のみを実装すると、次のようになります。
public class EntityReferenceCheckSet
{
    private static EntityReferenceCheckSet
      m_instance = new EntityReferenceCheckSet();

    //実体のシンボルテーブル
    private static GeTable m_table = GeTable.getInstance();
    
    //チェックセット
    HashSet<String> m_set;
    
    
    private EntityReferenceCheckSet()
    {
        m_set = new HashSet<String>();
    }
    
    public static EntityReferenceCheckSet getInstance()
    {
        return m_instance;
    }
    
    public void add(String refName)
    {
        m_set.add(refName);
    }
}

PeReferenceHelperの実装

EntityValueVisitorで使っているPeReferenceHelperについて説明します。
PeReferenceHelperはパラメータ実体参照の、出現位置による処理の違いを表現するクラスです。
EntityValueでパラメータ実体参照を認識したときの処理
 3.パラメータ実体の参照を認識したとき(修正あり)
 3-1.実体名をキーにパラメータ実体テーブルを検索する
 3-2.実体名がテーブルに無い場合
 3-2-1.スタンドアロン文書宣言がyesのとき
 3-2-1-1.★仕様なし
 3-2-2.スタンドアロン文書宣言がnoのとき
 3-2-2-1.外部サブセットが無い場合
 3-2-2-1-1.★仕様なし
 3-2-2-2.外部サブセットが有る場合
 3-2-2-2-1.妥当性制約違反エラー
 3-3.実体名がテーブルに有る場合
 3-3-1.再帰用のスタックに実体名を追加する
 3-3-2.置換テキストRepTextを取得する
 3-3-3.RepTextをReplacementTextで構文解析する
 3-3-4.パラメータ実体のトークン列の代わりに構文解析したトークン列を返す
を実装すると、次のようになります。
public class PeReferenceHelper
{
    //シングルトンパターン
    private static PeReferenceHelper m_instance
                             = new PeReferenceHelper();
    
    //パラメータ実体のシンボルテーブル
    private static PeTable m_peTable = PeTable.getInstance();
    
    //整形式制約をチェックするためのスタック
    private static NoRecursionPeEntityNameStack m_entityNameStack 
                     = NoRecursionPeEntityNameStack.getInstance();

    //トークンマネージャ
    private static TokenManager m_tokenManager
                                  = TokenManager.getInstance();
    
    //XMLDeclの内容
    private static XmlDeclData m_xmldecl = XmlDeclData.getInstance();
    
    //DOCTYPE宣言の内容
    private static DoctypeData m_doctype = DoctypeData.getInstance();

    //EBNFデータツリー replacementText
    private static EbnfNodeAcceptor m_replacementText
                      = SyntaxTree.getInstance().getReplacementText();
    
    private PeReferenceHelper()
    {
    }

    public static PeReferenceHelper getInstance()
    {
        return m_instance;
    }
    
    public String getReplacementTextInEntityValue(String refName)
    {
        String retval = null;
        
        //パラメータ参照テーブルから置換テキストを取得する
        String replacementStr = m_peTable.getData(refName);
        
        //実体名がテーブルに無い場合
        if (replacementStr == null)
        {
            //スタンドアロン文書宣言を取得する
            boolean standalone_yes = m_xmldecl.isStandalone();

            //スタンドアロン文書宣言がyesのとき
            if (standalone_yes)
            {
                //TODO:仕様を決める
            }
            //スタンドアロン文書宣言がnoのとき
            else
            {
                boolean hasExtSubset = m_doctype.hasExtSubset();
                
                 //外部サブセットが無い場合
                if (!hasExtSubset)
                {
                    //TODO:仕様を決める
                }
                //外部サブセットが有る場合
                else
                {
                     //TODO 妥当性制約違反エラー
                }
            }
        }
        //実体名がテーブルに有る場合
        else
        {
            //再帰用のスタックにpushする
            m_entityNameStack.push(refName);
            //コンテキストを切り替える
            m_tokenManager.setNewString(refName, replacementStr);
            {
                MakingReplacementTextVisitor v
                            = new MakingReplacementTextVisitor();
                
                //置換テキストの構文を構文解析する
                m_replacementText.accept(v);
                
                //置換テキストの構文解析済みの文字列を取得する
                retval = v.getText();
            }
            //スタックにつんでいたものを除去する
            m_tokenManager.finalize();
            m_entityNameStack.pop();
        }
        
        return retval;
    }
}

ここで、ReplacementTextの構文解析の前後に、第021回で説明したコンテキストの切り替えを実施しています。

DoctypedeclVisitor/DoctypeDataの修正

PeReferenceHelperで出てきた外部サブセットの有無情報の取得処理を第046回で実装したDoctypedeclVisitor/DoctypeDataに追加します。

DoctypedeclVisitor
public class DoctypedeclVisitor extends SyntaxParserVisitor
{
    ・・・・
    
    //追加
    private ExternalID m_externalId;
    
    ・・・・
    
    //修正
    public DoctypedeclVisitor()
    {
        m_externalId = null;
    }
    
    ・・・・
    
    //追加
    @Override
    public void visit(EbnfDocExternalIDNode n)
    {
        ExternalIDVisitor v = new ExternalIDVisitor();
        n.accept(v);
        
        m_result = v.getResult();
        
        if (m_result)
        {
            m_externalId = v.getExternalId();
            m_doctypedata.setExtSubsetOn();
        }
    }
    
}

DoctypeData
public class DoctypeData
{
    ・・・・
    //追加
    private boolean m_hasExtSubset;
    
    
    private DoctypeData()
    {
        ・・・・
        //追加
        m_hasExtSubset = false;
    }
    
    public static DoctypeData getInstance()
    {
        return m_instance;
    }
    
    ・・・・・
    
    //追加
    public void setExtSubsetOn()
    {
        m_hasExtSubset = true;
    }
    
    //追加
    public boolean hasExtSubset()
    {
        return m_hasExtSubset;
    }
}

ExternalIDの実装は、EntityValue内のExternalIDで工夫した部分です。Doctypedecl内でも処理用のEbnfDocExternalIDNodeを追加しています。

長くなりましたのでExternalIDの説明は次回にします。
Comment(0)

コメント

コメントを投稿する