001/*
002 * Copyright (c) 2004-2020 Tada AB and other contributors, as listed below.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the The BSD 3-Clause License
006 * which accompanies this distribution, and is available at
007 * http://opensource.org/licenses/BSD-3-Clause
008 *
009 * Contributors:
010 *   Tada AB
011 *   Purdue University
012 *   Chapman Flack
013 */
014package org.postgresql.pljava.example.annotation;
015
016import org.postgresql.pljava.annotation.Function;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.sql.ResultSet;
021import java.sql.SQLException;
022import java.util.Collections;
023import java.util.Iterator;
024import java.util.Map;
025import java.util.Properties;
026import java.util.ResourceBundle;
027import java.util.logging.Logger;
028
029import org.postgresql.pljava.ResultSetProvider;
030import org.postgresql.pljava.annotation.SQLAction;
031
032/**
033 * An example that retrieves a {@code Properties} resource, and returns
034 * (key,value) rows from it by implementing the {@code ResultSetProvider}
035 * interface.
036 * @author Thomas Hallgren
037 */
038@SQLAction(requires = {"propertyExampleAnno", "propertyExampleRB"}, install = {
039    "WITH" +
040    " expected AS (VALUES" +
041    "  ('adjective' ::varchar(200), 'avaricious' ::varchar(200))," +
042    "  ('noun',                     'platypus')" +
043    " )" +
044    "SELECT" +
045    "  CASE WHEN" +
046    "   2 = count(prop) AND every(prop IN (SELECT expected FROM expected))" +
047    "  THEN javatest.logmessage('INFO',    'get resource passes')" +
048    "  ELSE javatest.logmessage('WARNING', 'get resource fails')" +
049    "  END" +
050    " FROM" +
051    "  propertyExampleAnno() AS prop",
052
053    "WITH" +
054    " expected AS (VALUES" +
055    "  ('adjective' ::varchar(200), 'avaricious' ::varchar(200))," +
056    "  ('noun',                     'platypus')" +
057    " )" +
058    "SELECT" +
059    "  CASE WHEN" +
060    "   2 = count(prop) AND every(prop IN (SELECT expected FROM expected))" +
061    "  THEN javatest.logmessage('INFO',    'get ResourceBundle passes')" +
062    "  ELSE javatest.logmessage('WARNING', 'get ResourceBundle fails')" +
063    "  END" +
064    " FROM" +
065    "  propertyExampleRB() AS prop"
066})
067public class UsingProperties implements ResultSetProvider.Large
068{
069    private static Logger s_logger = Logger.getAnonymousLogger();
070    private final Iterator m_propertyIterator;
071    
072    public UsingProperties()
073    throws IOException
074    {
075        Properties v = new Properties();
076        InputStream propStream =
077            this.getClass().getResourceAsStream("example.properties");
078
079        if(propStream == null)
080        {
081            s_logger.fine("example.properties was null");
082            m_propertyIterator = Collections.EMPTY_SET.iterator();
083        }
084        else
085        {
086            v.load(propStream);
087            propStream.close();
088            s_logger.fine("example.properties has " + v.size() + " entries");
089            m_propertyIterator = v.entrySet().iterator();
090        }
091    }
092
093    /**
094     * This constructor (distinguished by signature) reads the same property
095     * file, but using the {@code ResourceBundle} machinery instead of
096     * {@code Properties}.
097     */
098    private UsingProperties(Void usingResourceBundle)
099    {
100        ResourceBundle b =
101            ResourceBundle.getBundle(getClass().getPackageName() + ".example");
102
103        Iterator<String> keys = b.getKeys().asIterator();
104
105        m_propertyIterator = new Iterator<Map.Entry<String,String>>()
106        {
107            public boolean hasNext()
108            {
109                return keys.hasNext();
110            }
111
112            public Map.Entry<String,String> next()
113            {
114                String k = keys.next();
115                return Map.entry(k, b.getString(k));
116            }
117        };
118    }
119
120    public boolean assignRowValues(ResultSet receiver, long currentRow)
121            throws SQLException
122    {
123        if(!m_propertyIterator.hasNext())
124        {
125            s_logger.fine("no more rows, returning false");
126            return false;
127        }
128        Map.Entry propEntry = (Map.Entry)m_propertyIterator.next();
129        receiver.updateString(1, (String)propEntry.getKey());
130        receiver.updateString(2, (String)propEntry.getValue());
131        // s_logger.fine("next row created, returning true");
132        return true;
133    }
134
135    /**
136     * Return the contents of the {@code example.properties} resource,
137     * one (key,value) row per entry.
138     */
139    @Function(type = "javatest._properties", provides = "propertyExampleAnno")
140    public static ResultSetProvider propertyExampleAnno()
141    throws SQLException
142    {
143        try
144        {
145            return new UsingProperties();
146        }
147        catch(IOException e)
148        {
149            throw new SQLException("Error reading properties", e.getMessage());
150        }
151    }
152
153    /**
154     * Return the contents of the {@code example.properties} resource,
155     * one (key,value) row per entry, using {@code ResourceBundle} to load it.
156     */
157    @Function(type = "javatest._properties", provides = "propertyExampleRB")
158    public static ResultSetProvider propertyExampleRB()
159    throws SQLException
160    {
161        return new UsingProperties(null);
162    }
163
164    public void close()
165    {
166    }
167}